wallet refactoring
Signed-off-by: Goran Jovic <goranjovic@gmail.com>
This commit is contained in:
parent
2b6d6722b7
commit
3e7c059b59
|
@ -265,7 +265,7 @@
|
||||||
view)
|
view)
|
||||||
|
|
||||||
(views/defview main-screen-modal-view [current-view & components]
|
(views/defview main-screen-modal-view [current-view & components]
|
||||||
(views/letsubs [signing? [:get-in [:wallet :send-transaction :signing?]]]
|
(views/letsubs [signing? [:get-in [:wallet :send-transaction :show-password-input?]]]
|
||||||
(let [main-screen-view (create-main-screen-view current-view)]
|
(let [main-screen-view (create-main-screen-view current-view)]
|
||||||
[main-screen-view styles/flex
|
[main-screen-view styles/flex
|
||||||
[keyboard-avoiding-view {:flex 1 :flex-direction :column}
|
[keyboard-avoiding-view {:flex 1 :flex-direction :column}
|
||||||
|
|
|
@ -16,7 +16,7 @@ dependencies {
|
||||||
implementation 'com.instabug.library:instabug:3+'
|
implementation 'com.instabug.library:instabug:3+'
|
||||||
implementation 'status-im:function:0.0.1'
|
implementation 'status-im:function:0.0.1'
|
||||||
|
|
||||||
String statusGoVersion = 'develop-ga6d69eba'
|
String statusGoVersion = 'tags-v0.11.0^0-g4afd9e6c-302'
|
||||||
final String statusGoGroup = 'status-im', statusGoName = 'status-go'
|
final String statusGoGroup = 'status-im', statusGoName = 'status-go'
|
||||||
|
|
||||||
// Check if the local status-go jar exists, and compile against that if it does
|
// Check if the local status-go jar exists, and compile against that if it does
|
||||||
|
|
|
@ -504,8 +504,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void approveSignRequest(final String id, final String password, final Callback callback) {
|
public void sendTransaction(final String txArgsJSON, final String password, final Callback callback) {
|
||||||
Log.d(TAG, "approveSignRequest");
|
Log.d(TAG, "sendTransaction");
|
||||||
if (!checkAvailability()) {
|
if (!checkAvailability()) {
|
||||||
callback.invoke(false);
|
callback.invoke(false);
|
||||||
return;
|
return;
|
||||||
|
@ -514,7 +514,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||||
Runnable r = new Runnable() {
|
Runnable r = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
String res = Statusgo.ApproveSignRequest(id, password);
|
String res = Statusgo.SendTransaction(txArgsJSON, password);
|
||||||
callback.invoke(res);
|
callback.invoke(res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -523,8 +523,8 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void approveSignRequestWithArgs(final String id, final String password, final String gas, final String gasPrice, final Callback callback) {
|
public void signMessage(final String rpcParams, final Callback callback) {
|
||||||
Log.d(TAG, "approveSignRequestWithArgs");
|
Log.d(TAG, "signMessage");
|
||||||
if (!checkAvailability()) {
|
if (!checkAvailability()) {
|
||||||
callback.invoke(false);
|
callback.invoke(false);
|
||||||
return;
|
return;
|
||||||
|
@ -533,7 +533,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||||
Runnable r = new Runnable() {
|
Runnable r = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
String res = Statusgo.ApproveSignRequestWithArgs(id, password, Long.parseLong(gas), Long.parseLong(gasPrice));
|
String res = Statusgo.SignMessage(rpcParams);
|
||||||
callback.invoke(res);
|
callback.invoke(res);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -541,24 +541,6 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||||
StatusThreadPoolExecutor.getInstance().execute(r);
|
StatusThreadPoolExecutor.getInstance().execute(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ReactMethod
|
|
||||||
public void discardSignRequest(final String id) {
|
|
||||||
Log.d(TAG, "discardSignRequest");
|
|
||||||
if (!checkAvailability()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Runnable r = new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Statusgo.DiscardSignRequest(id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
StatusThreadPoolExecutor.getInstance().execute(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void setAdjustResize() {
|
public void setAdjustResize() {
|
||||||
Log.d(TAG, "setAdjustResize");
|
Log.d(TAG, "setAdjustResize");
|
||||||
|
@ -667,7 +649,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void sendWeb3Request(final String payload, final Callback callback) {
|
public void callRPC(final String payload, final Callback callback) {
|
||||||
Runnable r = new Runnable() {
|
Runnable r = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -680,7 +662,7 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void sendWeb3PrivateRequest(final String payload, final Callback callback) {
|
public void callPrivateRPC(final String payload, final Callback callback) {
|
||||||
Runnable r = new Runnable() {
|
Runnable r = new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
|
@ -251,43 +251,30 @@ RCT_EXPORT_METHOD(login:(NSString *)address
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
#pragma mark - Approve Sign Request
|
#pragma mark - SendTransaction
|
||||||
//////////////////////////////////////////////////////////////////// approveSignRequests
|
//////////////////////////////////////////////////////////////////// sendTransaction
|
||||||
RCT_EXPORT_METHOD(approveSignRequest:(NSString *)id
|
RCT_EXPORT_METHOD(sendTransaction:(NSString *)txArgsJSON
|
||||||
password:(NSString *)password
|
password:(NSString *)password
|
||||||
callback:(RCTResponseSenderBlock)callback) {
|
callback:(RCTResponseSenderBlock)callback) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
NSLog(@"ApproveSignRequest() method called");
|
NSLog(@"SendTransaction() method called");
|
||||||
#endif
|
#endif
|
||||||
char * result = ApproveSignRequest((char *) [id UTF8String], (char *) [password UTF8String]);
|
char * result = SendTransaction((char *) [txArgsJSON UTF8String], (char *) [password UTF8String]);
|
||||||
callback(@[[NSString stringWithUTF8String: result]]);
|
callback(@[[NSString stringWithUTF8String: result]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
#pragma mark - Approve Sign Request With Args
|
#pragma mark - SignMessage
|
||||||
//////////////////////////////////////////////////////////////////// approveSignRequestWithArgs
|
//////////////////////////////////////////////////////////////////// signMessage
|
||||||
RCT_EXPORT_METHOD(approveSignRequestWithArgs:(NSString *)id
|
RCT_EXPORT_METHOD(signMessage:(NSString *)message
|
||||||
password:(NSString *)password
|
|
||||||
gas:(NSString *)gas
|
|
||||||
gasPrice:(NSString *)gasPrice
|
|
||||||
callback:(RCTResponseSenderBlock)callback) {
|
callback:(RCTResponseSenderBlock)callback) {
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
NSLog(@"ApproveSignRequestWithArgs() method called");
|
NSLog(@"SignMessage() method called");
|
||||||
#endif
|
#endif
|
||||||
char * result = ApproveSignRequestWithArgs((char *) [id UTF8String], (char *) [password UTF8String], (long long) [gas longLongValue], (long long) [gasPrice longLongValue]);
|
char * result = SignMessage((char *) [message UTF8String]);
|
||||||
callback(@[[NSString stringWithUTF8String: result]]);
|
callback(@[[NSString stringWithUTF8String: result]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
#pragma mark - Discard Sign Request
|
|
||||||
//////////////////////////////////////////////////////////////////// discardSignRequest
|
|
||||||
RCT_EXPORT_METHOD(discardSignRequest:(NSString *)id) {
|
|
||||||
#if DEBUG
|
|
||||||
NSLog(@"DiscardSignRequest() method called");
|
|
||||||
#endif
|
|
||||||
DiscardSignRequest((char *) [id UTF8String]);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
#pragma mark - only android methods
|
#pragma mark - only android methods
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
@ -329,7 +316,7 @@ RCT_EXPORT_METHOD(clearStorageAPIs) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(sendWeb3Request:(NSString *)payload
|
RCT_EXPORT_METHOD(callRPC:(NSString *)payload
|
||||||
callback:(RCTResponseSenderBlock)callback) {
|
callback:(RCTResponseSenderBlock)callback) {
|
||||||
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
char * result = CallRPC((char *) [payload UTF8String]);
|
char * result = CallRPC((char *) [payload UTF8String]);
|
||||||
|
@ -339,7 +326,7 @@ RCT_EXPORT_METHOD(sendWeb3Request:(NSString *)payload
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(sendWeb3PrivateRequest:(NSString *)payload
|
RCT_EXPORT_METHOD(callPrivateRPC:(NSString *)payload
|
||||||
callback:(RCTResponseSenderBlock)callback) {
|
callback:(RCTResponseSenderBlock)callback) {
|
||||||
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async( dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
char * result = CallPrivateRPC((char *) [payload UTF8String]);
|
char * result = CallPrivateRPC((char *) [payload UTF8String]);
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<artifactItem>
|
<artifactItem>
|
||||||
<groupId>status-im</groupId>
|
<groupId>status-im</groupId>
|
||||||
<artifactId>status-go-ios-simulator</artifactId>
|
<artifactId>status-go-ios-simulator</artifactId>
|
||||||
<version>develop-ga6d69eba</version>
|
<version>tags-v0.11.0^0-g4afd9e6c-302</version>
|
||||||
<type>zip</type>
|
<type>zip</type>
|
||||||
<overWrite>true</overWrite>
|
<overWrite>true</overWrite>
|
||||||
<outputDirectory>./</outputDirectory>
|
<outputDirectory>./</outputDirectory>
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,27 +2,31 @@ if(typeof StatusHttpProvider === "undefined"){
|
||||||
var callbackId = 0;
|
var callbackId = 0;
|
||||||
var callbacks = {};
|
var callbacks = {};
|
||||||
|
|
||||||
function httpCallback(id, data) {
|
WebViewBridge.onMessage = function (message) {
|
||||||
var result = data;
|
data = JSON.parse(message);
|
||||||
var error = null;
|
|
||||||
|
|
||||||
try {
|
if (data.type === "navigate-to-blank")
|
||||||
result = JSON.parse(data);
|
window.location.href = "about:blank";
|
||||||
} catch (e) {
|
|
||||||
error = {message: "InvalidResponse"};
|
else if (data.type === "status-api-success")
|
||||||
|
{
|
||||||
|
window.STATUS_API = data.data;
|
||||||
|
window.postMessage({ type: 'STATUS_API_SUCCESS', permissions: data.keys }, "*");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (data.type === "web3-send-async-callback")
|
||||||
|
{
|
||||||
|
var id = data.messageId;
|
||||||
if (callbacks[id]) {
|
if (callbacks[id]) {
|
||||||
callbacks[id](error, result);
|
callbacks[id](data.error, data.result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var StatusHttpProvider = function (host, timeout) {
|
|
||||||
this.host = host || 'http://localhost:8545';
|
|
||||||
this.timeout = timeout || 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var StatusHttpProvider = function () {};
|
||||||
|
|
||||||
StatusHttpProvider.prototype.isStatus = true;
|
StatusHttpProvider.prototype.isStatus = true;
|
||||||
|
StatusHttpProvider.prototype.isConnected = function () { return true; };
|
||||||
|
|
||||||
function web3Response (payload, result){
|
function web3Response (payload, result){
|
||||||
return {id: payload.id,
|
return {id: payload.id,
|
||||||
|
@ -66,57 +70,19 @@ StatusHttpProvider.prototype.sendAsync = function (payload, callback) {
|
||||||
else {
|
else {
|
||||||
var messageId = callbackId++;
|
var messageId = callbackId++;
|
||||||
callbacks[messageId] = callback;
|
callbacks[messageId] = callback;
|
||||||
if (typeof StatusBridge == "undefined") {
|
WebViewBridge.send(JSON.stringify({type: 'web3-send-async',
|
||||||
var data = {
|
messageId: messageId,
|
||||||
payload: JSON.stringify(payload),
|
payload: payload}));
|
||||||
callbackId: JSON.stringify(messageId),
|
|
||||||
host: this.host
|
|
||||||
};
|
|
||||||
|
|
||||||
webkit.messageHandlers.sendRequest.postMessage(JSON.stringify(data));
|
|
||||||
} else {
|
|
||||||
StatusBridge.sendRequest(this.host, JSON.stringify(messageId), JSON.stringify(payload));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
StatusHttpProvider.prototype.prepareRequest = function () {
|
|
||||||
var request = new XMLHttpRequest();
|
|
||||||
|
|
||||||
request.open('POST', this.host, false);
|
|
||||||
request.setRequestHeader('Content-Type', 'application/json');
|
|
||||||
return request;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronously tries to make Http request
|
|
||||||
*
|
|
||||||
* @method isConnected
|
|
||||||
* @return {Boolean} returns true if request haven't failed. Otherwise false
|
|
||||||
*/
|
|
||||||
StatusHttpProvider.prototype.isConnected = function () {
|
|
||||||
try {
|
|
||||||
this.sendAsync({
|
|
||||||
id: 9999999999,
|
|
||||||
jsonrpc: '2.0',
|
|
||||||
method: 'net_listening',
|
|
||||||
params: []
|
|
||||||
}, function () {
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var protocol = window.location.protocol
|
var protocol = window.location.protocol
|
||||||
var address = providerAddress || "http://localhost:8545";
|
|
||||||
console.log(protocol);
|
|
||||||
if (typeof web3 === "undefined") {
|
if (typeof web3 === "undefined") {
|
||||||
|
//why do we need this condition?
|
||||||
if (protocol == "https:" || protocol == "http:") {
|
if (protocol == "https:" || protocol == "http:") {
|
||||||
console.log("StatusHttpProvider");
|
console.log("StatusHttpProvider");
|
||||||
web3 = new Web3(new StatusHttpProvider(address));
|
web3 = new Web3(new StatusHttpProvider());
|
||||||
web3.eth.defaultAccount = currentAccountAddress;
|
web3.eth.defaultAccount = currentAccountAddress; // currentAccountAddress - injected from status-react
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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}
|
{:type [{:id :inbound :label (i18n/label :t/incoming) :checked? true}
|
||||||
{:id :outbound :label (i18n/label :t/outgoing) :checked? true}
|
{:id :outbound :label (i18n/label :t/outgoing) :checked? true}
|
||||||
{:id :pending :label (i18n/label :t/pending) :checked? true}
|
{:id :pending :label (i18n/label :t/pending) :checked? true}
|
||||||
{:id :failed :label (i18n/label :t/failed) :checked? true}
|
{:id :failed :label (i18n/label :t/failed) :checked? true}]}})
|
||||||
;; TODO(jeluard) Restore once we support postponing transaction
|
|
||||||
#_{:id :postponed :label (i18n/label :t/postponed) :checked? true}]}})
|
|
||||||
|
|
||||||
(def mainnet-networks
|
(def mainnet-networks
|
||||||
{"mainnet" {:id "mainnet",
|
{"mainnet" {:id "mainnet",
|
||||||
|
@ -250,11 +248,11 @@
|
||||||
;; Used to generate topic for contact discoveries
|
;; Used to generate topic for contact discoveries
|
||||||
(def contact-discovery "contact-discovery")
|
(def contact-discovery "contact-discovery")
|
||||||
|
|
||||||
(def ^:const send-transaction-no-error-code "0")
|
(def ^:const send-transaction-failed-parse-response 1)
|
||||||
(def ^:const send-transaction-default-error-code "1")
|
(def ^:const send-transaction-failed-parse-params 2)
|
||||||
(def ^:const send-transaction-password-error-code "2")
|
(def ^:const send-transaction-no-account-selected 3)
|
||||||
(def ^:const send-transaction-timeout-error-code "3")
|
(def ^:const send-transaction-invalid-tx-sender 4)
|
||||||
(def ^:const send-transaction-discarded-error-code "4")
|
(def ^:const send-transaction-err-decrypt 5)
|
||||||
|
|
||||||
(def ^:const web3-send-transaction "eth_sendTransaction")
|
(def ^:const web3-send-transaction "eth_sendTransaction")
|
||||||
(def ^:const web3-personal-sign "personal_sign")
|
(def ^:const web3-personal-sign "personal_sign")
|
||||||
|
@ -269,3 +267,5 @@
|
||||||
(def ^:const status-api-success "status-api-success")
|
(def ^:const status-api-success "status-api-success")
|
||||||
(def ^:const status-api-request "status-api-request")
|
(def ^:const status-api-request "status-api-request")
|
||||||
(def ^:const history-state-changed "history-state-changed")
|
(def ^:const history-state-changed "history-state-changed")
|
||||||
|
(def ^:const web3-send-async "web3-send-async")
|
||||||
|
(def ^:const web3-send-async-callback "web3-send-async-callback")
|
|
@ -4,7 +4,8 @@
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
[status-im.ui.screens.browser.default-dapps :as default-dapps]
|
[status-im.ui.screens.browser.default-dapps :as default-dapps]
|
||||||
[status-im.utils.http :as http]))
|
[status-im.utils.http :as http]
|
||||||
|
[re-frame.core :as re-frame]))
|
||||||
|
|
||||||
(defn get-current-url [{:keys [history history-index]}]
|
(defn get-current-url [{:keys [history history-index]}]
|
||||||
(when (and history-index history)
|
(when (and history-index history)
|
||||||
|
@ -72,7 +73,10 @@
|
||||||
(assoc (update-dapp-permissions-fx cofx {:dapp dapp-name
|
(assoc (update-dapp-permissions-fx cofx {:dapp dapp-name
|
||||||
:permissions (vec (set (concat (keys permissions-allowed)
|
:permissions (vec (set (concat (keys permissions-allowed)
|
||||||
user-permissions)))})
|
user-permissions)))})
|
||||||
:send-to-bridge-fx [permissions-allowed webview])))
|
:send-to-bridge-fx [{:type constants/status-api-success
|
||||||
|
:data permissions-allowed
|
||||||
|
:keys (keys permissions-allowed)}
|
||||||
|
webview])))
|
||||||
|
|
||||||
(defn next-permission [cofx params & [permission permissions-data]]
|
(defn next-permission [cofx params & [permission permissions-data]]
|
||||||
(request-permission
|
(request-permission
|
||||||
|
@ -83,3 +87,15 @@
|
||||||
|
|
||||||
(and permission permissions-data)
|
(and permission permissions-data)
|
||||||
(assoc-in [:permissions-allowed permission] (get permissions-data permission)))))
|
(assoc-in [:permissions-allowed permission] (get permissions-data permission)))))
|
||||||
|
|
||||||
|
(defn web3-send-async [{:keys [db]} {:keys [method] :as payload} message-id]
|
||||||
|
(if (or (= method constants/web3-send-transaction)
|
||||||
|
(= method constants/web3-personal-sign))
|
||||||
|
{:db (update-in db [:wallet :transactions-queue] conj {:message-id message-id :payload payload})
|
||||||
|
:dispatch [:check-dapps-transactions-queue]}
|
||||||
|
{:call-rpc [payload
|
||||||
|
#(re-frame/dispatch [:send-to-bridge
|
||||||
|
{:type constants/web3-send-async-callback
|
||||||
|
:messageId message-id
|
||||||
|
:error %1
|
||||||
|
:result %2}])]}))
|
|
@ -1,5 +1,12 @@
|
||||||
(ns status-im.models.wallet
|
(ns status-im.models.wallet
|
||||||
(:require [status-im.utils.money :as money]))
|
(:require [status-im.utils.money :as money]
|
||||||
|
[status-im.i18n :as i18n]
|
||||||
|
[status-im.utils.ethereum.core :as ethereum]
|
||||||
|
[status-im.constants :as constants]
|
||||||
|
[status-im.utils.ethereum.tokens :as tokens]
|
||||||
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
|
[status-im.utils.hex :as utils.hex]
|
||||||
|
[status-im.transport.utils :as transport.utils]))
|
||||||
|
|
||||||
(def min-gas-price-wei (money/bignumber 1))
|
(def min-gas-price-wei (money/bignumber 1))
|
||||||
|
|
||||||
|
@ -51,3 +58,114 @@
|
||||||
(defn edit-value
|
(defn edit-value
|
||||||
[key value {:keys [db]}]
|
[key value {:keys [db]}]
|
||||||
{:db (update-in db [:wallet :edit] build-edit key value)})
|
{:db (update-in db [:wallet :edit] build-edit key value)})
|
||||||
|
|
||||||
|
;; DAPP TRANSACTION -> SEND TRANSACTION
|
||||||
|
(defn prepare-dapp-transaction [{{:keys [id method params]} :payload :as queued-transaction} contacts]
|
||||||
|
(let [{:keys [to value data gas gasPrice nonce]} (first params)
|
||||||
|
contact (get contacts (utils.hex/normalize-hex to))]
|
||||||
|
(cond-> {:id (str id)
|
||||||
|
:to-name (or (when (nil? to)
|
||||||
|
(i18n/label :t/new-contract))
|
||||||
|
contact)
|
||||||
|
:symbol :ETH
|
||||||
|
:method method
|
||||||
|
:dapp-transaction queued-transaction
|
||||||
|
:to to
|
||||||
|
:amount (money/bignumber (or value 0))
|
||||||
|
:gas (cond
|
||||||
|
gas
|
||||||
|
(money/bignumber gas)
|
||||||
|
value
|
||||||
|
(money/bignumber 21000))
|
||||||
|
:gas-price (when gasPrice
|
||||||
|
(money/bignumber gasPrice))
|
||||||
|
:data data}
|
||||||
|
nonce
|
||||||
|
(assoc :nonce nonce))))
|
||||||
|
|
||||||
|
;; SEND TRANSACTION -> RPC TRANSACTION
|
||||||
|
(defn prepare-send-transaction [from {:keys [amount to gas gas-price data nonce]}]
|
||||||
|
(cond-> {:from (ethereum/normalized-address from)
|
||||||
|
:to (ethereum/normalized-address to)
|
||||||
|
:value (ethereum/int->hex amount)
|
||||||
|
:gas (ethereum/int->hex gas)
|
||||||
|
:gas-price (ethereum/int->hex gas-price)}
|
||||||
|
data
|
||||||
|
(assoc :data data)
|
||||||
|
nonce
|
||||||
|
(assoc :nonce nonce)))
|
||||||
|
|
||||||
|
;; NOTE (andrey) we need this function, because params may be mixed up, so we need to figure out which one is address
|
||||||
|
;; and which message
|
||||||
|
(defn normalize-sign-message-params [params]
|
||||||
|
(let [first_param (first params)
|
||||||
|
second_param (second params)
|
||||||
|
first-param-address? (ethereum/address? first_param)
|
||||||
|
second-param-address? (ethereum/address? second_param)]
|
||||||
|
(when (or first-param-address? second-param-address?)
|
||||||
|
(if first-param-address?
|
||||||
|
[first_param second_param]
|
||||||
|
[second_param first_param]))))
|
||||||
|
|
||||||
|
(defn web3-error-callback [fx {:keys [webview-bridge]} {:keys [message-id]} message]
|
||||||
|
(assoc fx :send-to-bridge-fx [{:type constants/web3-send-async-callback
|
||||||
|
:messageId message-id
|
||||||
|
:error message}
|
||||||
|
webview-bridge]))
|
||||||
|
|
||||||
|
(defn dapp-complete-transaction [id result method message-id webview]
|
||||||
|
(cond-> {:send-to-bridge-fx [{:type constants/web3-send-async-callback
|
||||||
|
:messageId message-id
|
||||||
|
:result {:jsonrpc "2.0"
|
||||||
|
:id (int id)
|
||||||
|
:result result}}
|
||||||
|
webview]
|
||||||
|
:dispatch [:navigate-back]}
|
||||||
|
|
||||||
|
(= method constants/web3-send-transaction)
|
||||||
|
(assoc :dispatch-later [{:ms 400 :dispatch [:navigate-to-modal :wallet-transaction-sent-modal]}])))
|
||||||
|
|
||||||
|
(defn discard-transaction
|
||||||
|
[{:keys [db]}]
|
||||||
|
(let [{:keys [dapp-transaction]} (get-in db [:wallet :send-transaction])]
|
||||||
|
(cond-> {:db (assoc-in db [:wallet :send-transaction] {})}
|
||||||
|
dapp-transaction
|
||||||
|
(web3-error-callback db dapp-transaction "discarded"))))
|
||||||
|
|
||||||
|
(defn prepare-unconfirmed-transaction [db now hash]
|
||||||
|
(let [transaction (get-in db [:wallet :send-transaction])]
|
||||||
|
(let [chain (:chain db)
|
||||||
|
token (tokens/symbol->token (keyword chain) (:symbol transaction))]
|
||||||
|
(-> transaction
|
||||||
|
(assoc :confirmations "0"
|
||||||
|
:timestamp (str now)
|
||||||
|
:type :outbound
|
||||||
|
:hash hash)
|
||||||
|
(update :gas-price str)
|
||||||
|
(assoc :value (:amount transaction))
|
||||||
|
(assoc :token token)
|
||||||
|
(update :gas str)
|
||||||
|
(dissoc :message-id :id)))))
|
||||||
|
|
||||||
|
(defn handle-transaction-error [db {:keys [code message]}]
|
||||||
|
(let [{:keys [dapp-transaction]} (get-in db [:wallet :send-transaction])]
|
||||||
|
(case code
|
||||||
|
|
||||||
|
;;WRONG PASSWORD
|
||||||
|
constants/send-transaction-err-decrypt
|
||||||
|
{:db (-> db
|
||||||
|
(assoc-in [:wallet :send-transaction :wrong-password?] true))}
|
||||||
|
|
||||||
|
(cond-> {:db (-> db
|
||||||
|
navigation/navigate-back
|
||||||
|
(assoc-in [:wallet :transactions-queue] nil)
|
||||||
|
(assoc-in [:wallet :send-transaction] {}))
|
||||||
|
:wallet/show-transaction-error message}
|
||||||
|
|
||||||
|
dapp-transaction
|
||||||
|
(web3-error-callback db dapp-transaction message)))))
|
||||||
|
|
||||||
|
(defn transform-data-for-message [{:keys [method] :as transaction}]
|
||||||
|
(cond-> transaction
|
||||||
|
(= method constants/web3-personal-sign)
|
||||||
|
(update :data transport.utils/to-utf8)))
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
|
|
||||||
(def adjust-resize 16)
|
(def adjust-resize 16)
|
||||||
|
|
||||||
(defn move-to-internal-storage [callback]
|
|
||||||
(native-module/move-to-internal-storage callback))
|
|
||||||
|
|
||||||
(defn start-node [config]
|
(defn start-node [config]
|
||||||
(native-module/start-node config))
|
(native-module/start-node config))
|
||||||
|
|
||||||
|
@ -21,33 +18,27 @@
|
||||||
(defn login [address password callback]
|
(defn login [address password callback]
|
||||||
(native-module/login address password callback))
|
(native-module/login address password callback))
|
||||||
|
|
||||||
(defn approve-sign-request [id password callback]
|
|
||||||
(native-module/approve-sign-request id password callback))
|
|
||||||
|
|
||||||
(defn approve-sign-request-with-args [id password gas gas-price callback]
|
|
||||||
(native-module/approve-sign-request-with-args id password gas gas-price callback))
|
|
||||||
|
|
||||||
(defn discard-sign-request [id]
|
|
||||||
(native-module/discard-sign-request id))
|
|
||||||
|
|
||||||
(defn set-soft-input-mode [mode]
|
(defn set-soft-input-mode [mode]
|
||||||
(native-module/set-soft-input-mode mode))
|
(native-module/set-soft-input-mode mode))
|
||||||
|
|
||||||
(defn clear-web-data []
|
(defn clear-web-data []
|
||||||
(native-module/clear-web-data))
|
(native-module/clear-web-data))
|
||||||
|
|
||||||
(defn call-web3 [payload callback]
|
(defn call-rpc [payload callback]
|
||||||
(native-module/call-web3 payload callback))
|
(native-module/call-rpc payload callback))
|
||||||
|
|
||||||
(defn call-web3-private [payload callback]
|
(defn call-private-rpc [payload callback]
|
||||||
(native-module/call-web3-private payload callback))
|
(native-module/call-private-rpc payload callback))
|
||||||
|
|
||||||
|
(defn sign-message [rpcParams callback]
|
||||||
|
(native-module/sign-message rpcParams callback))
|
||||||
|
|
||||||
|
(defn send-transaction [rpcParams password callback]
|
||||||
|
(native-module/send-transaction rpcParams password callback))
|
||||||
|
|
||||||
(defn module-initialized! []
|
(defn module-initialized! []
|
||||||
(native-module/module-initialized!))
|
(native-module/module-initialized!))
|
||||||
|
|
||||||
(defn should-move-to-internal-storage? [callback]
|
|
||||||
(native-module/should-move-to-internal-storage? callback))
|
|
||||||
|
|
||||||
(defn notify-users [m callback]
|
(defn notify-users [m callback]
|
||||||
(native-module/notify-users m callback))
|
(native-module/notify-users m callback))
|
||||||
|
|
||||||
|
|
|
@ -54,14 +54,6 @@
|
||||||
(.addListener r/device-event-emitter "gethEvent"
|
(.addListener r/device-event-emitter "gethEvent"
|
||||||
#(re-frame/dispatch [:signal-event (.-jsonEvent %)])))
|
#(re-frame/dispatch [:signal-event (.-jsonEvent %)])))
|
||||||
|
|
||||||
(defn should-move-to-internal-storage? [on-result]
|
|
||||||
(when status
|
|
||||||
(call-module #(.shouldMoveToInternalStorage status on-result))))
|
|
||||||
|
|
||||||
(defn move-to-internal-storage [on-result]
|
|
||||||
(when status
|
|
||||||
(call-module #(.moveToInternalStorage status on-result))))
|
|
||||||
|
|
||||||
(defn stop-node []
|
(defn stop-node []
|
||||||
(when status
|
(when status
|
||||||
(call-module #(.stopNode status))))
|
(call-module #(.stopNode status))))
|
||||||
|
@ -101,27 +93,6 @@
|
||||||
(when status
|
(when status
|
||||||
(call-module #(.login status address password on-result))))
|
(call-module #(.login status address password on-result))))
|
||||||
|
|
||||||
(defn approve-sign-request
|
|
||||||
[id password callback]
|
|
||||||
(log/debug :approve-sign-request (boolean status) id)
|
|
||||||
(when status
|
|
||||||
(call-module #(.approveSignRequest status id password callback))))
|
|
||||||
|
|
||||||
(defn approve-sign-request-with-args
|
|
||||||
[id password gas gas-price callback]
|
|
||||||
(log/debug :approve-sign-request-with-args (boolean status) id gas gas-price)
|
|
||||||
(when status
|
|
||||||
(call-module #(.approveSignRequestWithArgs status id password gas gas-price callback))))
|
|
||||||
|
|
||||||
(defn discard-sign-request
|
|
||||||
[id]
|
|
||||||
(log/debug :discard-sign-request id)
|
|
||||||
(when status
|
|
||||||
(call-module #(.discardSignRequest status id))))
|
|
||||||
|
|
||||||
(defn- append-catalog-init [js]
|
|
||||||
(str js "\n" "var catalog = JSON.stringify(_status_catalog); catalog;"))
|
|
||||||
|
|
||||||
(defn set-soft-input-mode [mode]
|
(defn set-soft-input-mode [mode]
|
||||||
(when status
|
(when status
|
||||||
(call-module #(.setSoftInputMode status mode))))
|
(call-module #(.setSoftInputMode status mode))))
|
||||||
|
@ -131,13 +102,21 @@
|
||||||
(call-module #(.clearCookies status))
|
(call-module #(.clearCookies status))
|
||||||
(call-module #(.clearStorageAPIs status))))
|
(call-module #(.clearStorageAPIs status))))
|
||||||
|
|
||||||
(defn call-web3 [payload callback]
|
(defn call-rpc [payload callback]
|
||||||
(when status
|
(when status
|
||||||
(call-module #(.sendWeb3Request status payload callback))))
|
(call-module #(.callRPC status payload callback))))
|
||||||
|
|
||||||
(defn call-web3-private [payload callback]
|
(defn call-private-rpc [payload callback]
|
||||||
(when status
|
(when status
|
||||||
(call-module #(.sendWeb3PrivateRequest status payload callback))))
|
(call-module #(.callPrivateRPC status payload callback))))
|
||||||
|
|
||||||
|
(defn sign-message [rpcParams callback]
|
||||||
|
(when status
|
||||||
|
(call-module #(.signMessage status rpcParams callback))))
|
||||||
|
|
||||||
|
(defn send-transaction [rpcParams password callback]
|
||||||
|
(when status
|
||||||
|
(call-module #(.sendTransaction status rpcParams password callback))))
|
||||||
|
|
||||||
(defn close-application []
|
(defn close-application []
|
||||||
(.closeApplication status))
|
(.closeApplication status))
|
||||||
|
|
|
@ -11,7 +11,10 @@
|
||||||
[status-im.models.browser :as model]
|
[status-im.models.browser :as model]
|
||||||
[status-im.utils.platform :as platform]
|
[status-im.utils.platform :as platform]
|
||||||
[status-im.utils.utils :as utils]
|
[status-im.utils.utils :as utils]
|
||||||
[status-im.constants :as constants]))
|
[status-im.constants :as constants]
|
||||||
|
[status-im.native-module.core :as status]
|
||||||
|
[taoensso.timbre :as log]
|
||||||
|
[status-im.utils.types :as types]))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
:browse
|
:browse
|
||||||
|
@ -20,12 +23,22 @@
|
||||||
(utils.universal-links/open! link)
|
(utils.universal-links/open! link)
|
||||||
(list-selection/browse link))))
|
(list-selection/browse link))))
|
||||||
|
|
||||||
|
(re-frame/reg-fx
|
||||||
|
:call-rpc
|
||||||
|
(fn [[payload callback]]
|
||||||
|
(status/call-rpc
|
||||||
|
(types/clj->json payload)
|
||||||
|
(fn [response]
|
||||||
|
(if (= "" response)
|
||||||
|
(do
|
||||||
|
(log/warn :web3-response-error)
|
||||||
|
(callback "web3-response-error" nil))
|
||||||
|
(callback nil (.parse js/JSON response)))))))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
:send-to-bridge-fx
|
:send-to-bridge-fx
|
||||||
(fn [[permissions-allowed webview]]
|
(fn [[message webview]]
|
||||||
(.sendToBridge @webview (.stringify js/JSON (clj->js {:type constants/status-api-success
|
(.sendToBridge webview (types/clj->json message))))
|
||||||
:data permissions-allowed
|
|
||||||
:keys (keys permissions-allowed)})))))
|
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
:show-dapp-permission-confirmation-fx
|
:show-dapp-permission-confirmation-fx
|
||||||
|
@ -67,6 +80,12 @@
|
||||||
:history-index 0
|
:history-index 0
|
||||||
:history [normalized-url]}))))
|
:history [normalized-url]}))))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:send-to-bridge
|
||||||
|
[re-frame/trim-v]
|
||||||
|
(fn [cofx [message]]
|
||||||
|
{:send-to-bridge-fx [message (get-in cofx [:db :webview-bridge])]}))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:open-browser
|
:open-browser
|
||||||
[re-frame/trim-v]
|
[re-frame/trim-v]
|
||||||
|
@ -112,12 +131,20 @@
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:on-bridge-message
|
:on-bridge-message
|
||||||
[re-frame/trim-v]
|
[re-frame/trim-v]
|
||||||
(fn [{:keys [db] :as cofx} [{{:keys [url]} :navState :keys [type host permissions]} browser webview]]
|
(fn [{:keys [db] :as cofx} [message]]
|
||||||
|
(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
|
(cond
|
||||||
|
|
||||||
(and (= type constants/history-state-changed) platform/ios? (not= "about:blank" url))
|
(and (= type constants/history-state-changed) platform/ios? (not= "about:blank" url))
|
||||||
(model/update-browser-history-fx cofx browser url false)
|
(model/update-browser-history-fx cofx browser url false)
|
||||||
|
|
||||||
|
(= type constants/web3-send-async)
|
||||||
|
(model/web3-send-async cofx payload messageId)
|
||||||
|
|
||||||
(= type constants/status-api-request)
|
(= type constants/status-api-request)
|
||||||
(let [{:account/keys [account]} db
|
(let [{:account/keys [account]} db
|
||||||
{:keys [dapp? name]} browser
|
{:keys [dapp? name]} browser
|
||||||
|
@ -125,11 +152,11 @@
|
||||||
(model/request-permission
|
(model/request-permission
|
||||||
cofx
|
cofx
|
||||||
{:dapp-name dapp-name
|
{:dapp-name dapp-name
|
||||||
:webview webview
|
:webview webview-bridge
|
||||||
:index 0
|
:index 0
|
||||||
:user-permissions (get-in db [:dapps/permissions dapp-name :permissions])
|
:user-permissions (get-in db [:dapps/permissions dapp-name :permissions])
|
||||||
:requested-permissions permissions
|
:requested-permissions permissions
|
||||||
:permissions-data {constants/dapp-permission-contact-code (:public-key account)}})))))
|
:permissions-data {constants/dapp-permission-contact-code (:public-key account)}}))))))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:next-dapp-permission
|
:next-dapp-permission
|
||||||
|
|
|
@ -103,18 +103,16 @@
|
||||||
[components.webview-bridge/webview-bridge
|
[components.webview-bridge/webview-bridge
|
||||||
{:dapp? dapp?
|
{:dapp? dapp?
|
||||||
:dapp-name name
|
:dapp-name name
|
||||||
:ref #(reset! webview %)
|
:ref #(do
|
||||||
|
(reset! webview %)
|
||||||
|
(re-frame/dispatch [:set :webview-bridge %]))
|
||||||
:source {:uri url}
|
:source {:uri url}
|
||||||
:java-script-enabled true
|
:java-script-enabled true
|
||||||
:bounces false
|
:bounces false
|
||||||
:local-storage-enabled true
|
:local-storage-enabled true
|
||||||
:render-error web-view-error
|
:render-error web-view-error
|
||||||
:on-navigation-state-change #(on-navigation-change % browser)
|
:on-navigation-state-change #(on-navigation-change % browser)
|
||||||
:on-bridge-message #(re-frame/dispatch [:on-bridge-message
|
:on-bridge-message #(re-frame/dispatch [:on-bridge-message %])
|
||||||
(js->clj (.parse js/JSON %)
|
|
||||||
:keywordize-keys true)
|
|
||||||
browser
|
|
||||||
webview])
|
|
||||||
:on-load #(re-frame/dispatch [:update-browser-options {:error? false}])
|
:on-load #(re-frame/dispatch [:update-browser-options {:error? false}])
|
||||||
:on-error #(re-frame/dispatch [:update-browser-options {:error? true
|
:on-error #(re-frame/dispatch [:update-browser-options {:error? true
|
||||||
:loading? false}])
|
:loading? false}])
|
||||||
|
|
|
@ -364,11 +364,6 @@
|
||||||
(re-frame/dispatch [:fetch-web3-node-version-callback resp])))))
|
(re-frame/dispatch [:fetch-web3-node-version-callback resp])))))
|
||||||
nil))
|
nil))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:webview-geo-permissions-granted
|
|
||||||
(fn [{{:keys [webview-bridge]} :db} _]
|
|
||||||
(.geoPermissionsGranted webview-bridge)))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:get-fcm-token
|
:get-fcm-token
|
||||||
(fn [_ _]
|
(fn [_ _]
|
||||||
|
@ -393,8 +388,6 @@
|
||||||
(instabug/log (str "Signal event: " event-str))
|
(instabug/log (str "Signal event: " event-str))
|
||||||
(let [{:keys [type event]} (types/json->clj event-str)
|
(let [{:keys [type event]} (types/json->clj event-str)
|
||||||
to-dispatch (case type
|
to-dispatch (case type
|
||||||
"sign-request.queued" [:sign-request-queued event]
|
|
||||||
"sign-request.failed" [:sign-request-failed event]
|
|
||||||
"node.started" [:status-node-started]
|
"node.started" [:status-node-started]
|
||||||
"node.stopped" [:status-node-stopped]
|
"node.stopped" [:status-node-stopped]
|
||||||
"module.initialized" [:status-module-initialized]
|
"module.initialized" [:status-module-initialized]
|
||||||
|
|
|
@ -28,11 +28,6 @@
|
||||||
(fn [db [_ path]]
|
(fn [db [_ path]]
|
||||||
(get-in db path)))
|
(get-in db path)))
|
||||||
|
|
||||||
(reg-sub :signed-up?
|
|
||||||
:<- [:get-current-account]
|
|
||||||
(fn [current-account]
|
|
||||||
(:signed-up? current-account)))
|
|
||||||
|
|
||||||
(reg-sub :network
|
(reg-sub :network
|
||||||
:<- [:get-current-account]
|
:<- [:get-current-account]
|
||||||
(fn [current-account]
|
(fn [current-account]
|
||||||
|
|
|
@ -32,10 +32,10 @@
|
||||||
[status-im.ui.screens.wallet.request.views :refer [request-transaction send-transaction-request]]
|
[status-im.ui.screens.wallet.request.views :refer [request-transaction send-transaction-request]]
|
||||||
[status-im.ui.screens.wallet.components.views :as wallet.components]
|
[status-im.ui.screens.wallet.components.views :as wallet.components]
|
||||||
[status-im.ui.screens.wallet.onboarding.setup.views :as wallet.onboarding.setup]
|
[status-im.ui.screens.wallet.onboarding.setup.views :as wallet.onboarding.setup]
|
||||||
[status-im.ui.screens.wallet.send.views :as wallet.send]
|
[status-im.ui.screens.wallet.transaction-fee.views :as wallet.transaction-fee]
|
||||||
[status-im.ui.screens.wallet.settings.views :as wallet-settings]
|
[status-im.ui.screens.wallet.settings.views :as wallet-settings]
|
||||||
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
|
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
|
||||||
[status-im.ui.screens.wallet.send.transaction-sent.views :refer [transaction-sent transaction-sent-modal]]
|
[status-im.ui.screens.wallet.transaction-sent.views :refer [transaction-sent transaction-sent-modal]]
|
||||||
[status-im.ui.screens.wallet.components.views :refer [contact-code recent-recipients recipient-qr-code]]
|
[status-im.ui.screens.wallet.components.views :refer [contact-code recent-recipients recipient-qr-code]]
|
||||||
[status-im.ui.screens.network-settings.views :refer [network-settings]]
|
[status-im.ui.screens.network-settings.views :refer [network-settings]]
|
||||||
[status-im.ui.screens.network-settings.network-details.views :refer [network-details]]
|
[status-im.ui.screens.network-settings.network-details.views :refer [network-details]]
|
||||||
|
@ -110,7 +110,7 @@
|
||||||
:wallet-send-transaction-modal send-transaction-modal
|
:wallet-send-transaction-modal send-transaction-modal
|
||||||
:wallet-transaction-sent-modal transaction-sent-modal
|
:wallet-transaction-sent-modal transaction-sent-modal
|
||||||
:wallet-sign-message-modal sign-message-modal
|
:wallet-sign-message-modal sign-message-modal
|
||||||
:wallet-transaction-fee wallet.send/transaction-fee
|
:wallet-transaction-fee wallet.transaction-fee/transaction-fee
|
||||||
:wallet-onboarding-setup-modal wallet.onboarding.setup/modal
|
:wallet-onboarding-setup-modal wallet.onboarding.setup/modal
|
||||||
[react/view [react/text (str "Unknown modal view: " modal-view)]]))
|
[react/view [react/text (str "Unknown modal view: " modal-view)]]))
|
||||||
|
|
||||||
|
@ -135,8 +135,7 @@
|
||||||
[component]])]])))
|
[component]])]])))
|
||||||
|
|
||||||
(defview main []
|
(defview main []
|
||||||
(letsubs [signed-up? [:signed-up?]
|
(letsubs [view-id [:get :view-id]]
|
||||||
view-id [:get :view-id]]
|
|
||||||
{:component-did-mount utils.universal-links/initialize
|
{:component-did-mount utils.universal-links/initialize
|
||||||
:component-will-unmount utils.universal-links/finalize
|
:component-will-unmount utils.universal-links/finalize
|
||||||
:component-will-update (fn [] (react/dismiss-keyboard!))}
|
:component-will-update (fn [] (react/dismiss-keyboard!))}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
(ns status-im.ui.screens.wallet.db
|
(ns status-im.ui.screens.wallet.db
|
||||||
|
(:require-macros [status-im.utils.db :refer [allowed-keys]])
|
||||||
(:require [cljs.spec.alpha :as spec]
|
(:require [cljs.spec.alpha :as spec]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
status-im.ui.screens.wallet.request.db
|
status-im.ui.screens.wallet.request.db
|
||||||
|
@ -7,14 +8,36 @@
|
||||||
|
|
||||||
(spec/def :wallet.send/recipient string?)
|
(spec/def :wallet.send/recipient string?)
|
||||||
|
|
||||||
(spec/def :wallet/send (spec/keys :req-un [:wallet.send/recipient]))
|
(spec/def :wallet/send (allowed-keys :req-un [:wallet.send/recipient]))
|
||||||
|
|
||||||
(spec/def :wallet/wallet (spec/keys :opt-un [:wallet/send-transaction :wallet/request-transaction
|
(spec/def :wallet/balance-loading? (spec/nilable boolean?))
|
||||||
:wallet/transactions-queue]))
|
(spec/def :wallet/transactions-loading? (spec/nilable boolean?))
|
||||||
|
(spec/def :wallet/transactions-sync-started? (spec/nilable boolean?))
|
||||||
;; Placeholder namespace for wallet specs, which are a WIP depending on data
|
(spec/def :wallet/errors (spec/nilable any?))
|
||||||
;; model we decide on for balances, prices, etc.
|
(spec/def :wallet/transactions-last-updated-at (spec/nilable any?))
|
||||||
|
(spec/def :wallet/chat-transactions (spec/nilable any?))
|
||||||
|
(spec/def :wallet/transactions (spec/nilable any?))
|
||||||
|
(spec/def :wallet/transactions-queue (spec/nilable any?))
|
||||||
|
(spec/def :wallet/edit (spec/nilable any?))
|
||||||
|
(spec/def :wallet/current-tab (spec/nilable any?))
|
||||||
|
(spec/def :wallet/current-transaction (spec/nilable any?))
|
||||||
|
(spec/def :wallet/modal-history? (spec/nilable any?))
|
||||||
|
(spec/def :wallet/visible-tokens (spec/nilable any?))
|
||||||
|
(spec/def :wallet/currency (spec/nilable any?))
|
||||||
|
(spec/def :wallet/balance (spec/nilable any?))
|
||||||
|
|
||||||
|
(spec/def :wallet/wallet (allowed-keys :opt-un [:wallet/send-transaction :wallet/request-transaction
|
||||||
|
:wallet/transactions-queue
|
||||||
|
:wallet/balance-loading? :wallet/errors :wallet/transactions-loading?
|
||||||
|
:wallet/transactions-last-updated-at :wallet/chat-transactions
|
||||||
|
:wallet/transactions-sync-started? :wallet/transactions
|
||||||
|
:wallet/edit
|
||||||
|
:wallet/current-tab
|
||||||
|
:wallet/current-transaction
|
||||||
|
:wallet/modal-history?
|
||||||
|
:wallet/visible-tokens
|
||||||
|
:wallet/currency
|
||||||
|
:wallet/balance]))
|
||||||
|
|
||||||
(defn- too-precise-amount?
|
(defn- too-precise-amount?
|
||||||
"Checks if number has any extra digit beyond the allowed number of decimals.
|
"Checks if number has any extra digit beyond the allowed number of decimals.
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
(ns status-im.ui.screens.wallet.navigation
|
(ns status-im.ui.screens.wallet.navigation
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
[status-im.ui.screens.navigation :as navigation]
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
[status-im.utils.ethereum.core :as ethereum]))
|
[status-im.utils.ethereum.core :as ethereum]
|
||||||
|
[status-im.constants :as constants]))
|
||||||
|
|
||||||
(defmethod navigation/preload-data! :wallet
|
(defmethod navigation/preload-data! :wallet
|
||||||
[db _]
|
[db _]
|
||||||
|
@ -22,6 +23,7 @@
|
||||||
(def transaction-send-default
|
(def transaction-send-default
|
||||||
(let [symbol :ETH]
|
(let [symbol :ETH]
|
||||||
{:gas (ethereum/estimate-gas symbol)
|
{:gas (ethereum/estimate-gas symbol)
|
||||||
|
:method constants/web3-send-transaction
|
||||||
:symbol symbol}))
|
:symbol symbol}))
|
||||||
|
|
||||||
(def transaction-request-default
|
(def transaction-request-default
|
||||||
|
|
|
@ -4,8 +4,16 @@
|
||||||
[status-im.utils.money :as money]
|
[status-im.utils.money :as money]
|
||||||
[status-im.utils.security :as security]))
|
[status-im.utils.security :as security]))
|
||||||
|
|
||||||
(spec/def ::amount (spec/nilable money/valid?))
|
; transaction
|
||||||
|
(spec/def ::from (spec/nilable string?))
|
||||||
(spec/def ::to (spec/nilable string?))
|
(spec/def ::to (spec/nilable string?))
|
||||||
|
(spec/def ::amount (spec/nilable money/valid?))
|
||||||
|
(spec/def ::gas (spec/nilable money/valid?))
|
||||||
|
(spec/def ::gas-price (spec/nilable money/valid?))
|
||||||
|
; dapp transaction
|
||||||
|
(spec/def ::data (spec/nilable string?))
|
||||||
|
(spec/def ::nonce (spec/nilable money/valid?))
|
||||||
|
|
||||||
(spec/def ::to-name (spec/nilable string?))
|
(spec/def ::to-name (spec/nilable string?))
|
||||||
(spec/def ::amount-error (spec/nilable string?))
|
(spec/def ::amount-error (spec/nilable string?))
|
||||||
(spec/def ::asset-error (spec/nilable string?))
|
(spec/def ::asset-error (spec/nilable string?))
|
||||||
|
@ -13,25 +21,22 @@
|
||||||
(spec/def ::password (spec/nilable #(instance? security/MaskedData %)))
|
(spec/def ::password (spec/nilable #(instance? security/MaskedData %)))
|
||||||
(spec/def ::wrong-password? (spec/nilable boolean?))
|
(spec/def ::wrong-password? (spec/nilable boolean?))
|
||||||
(spec/def ::id (spec/nilable string?))
|
(spec/def ::id (spec/nilable string?))
|
||||||
(spec/def ::waiting-signal? (spec/nilable boolean?))
|
(spec/def ::show-password-input? (spec/nilable boolean?))
|
||||||
(spec/def ::signing? (spec/nilable boolean?))
|
|
||||||
(spec/def ::later? (spec/nilable boolean?))
|
|
||||||
(spec/def ::height double?)
|
(spec/def ::height double?)
|
||||||
(spec/def ::width double?)
|
(spec/def ::width double?)
|
||||||
(spec/def ::camera-flashlight #{:on :off})
|
(spec/def ::camera-flashlight #{:on :off})
|
||||||
(spec/def ::in-progress? boolean?)
|
(spec/def ::in-progress? boolean?)
|
||||||
(spec/def ::from-chat? (spec/nilable boolean?))
|
(spec/def ::from-chat? (spec/nilable boolean?))
|
||||||
(spec/def ::symbol (spec/nilable keyword?))
|
(spec/def ::symbol (spec/nilable keyword?))
|
||||||
(spec/def ::gas (spec/nilable money/valid?))
|
|
||||||
(spec/def ::gas-price (spec/nilable money/valid?))
|
|
||||||
(spec/def ::advanced? boolean?)
|
(spec/def ::advanced? boolean?)
|
||||||
(spec/def ::whisper-identity (spec/nilable string?))
|
(spec/def ::whisper-identity (spec/nilable string?))
|
||||||
(spec/def ::method (spec/nilable string?))
|
(spec/def ::method (spec/nilable string?))
|
||||||
(spec/def ::tx-hash (spec/nilable string?))
|
(spec/def ::tx-hash (spec/nilable string?))
|
||||||
|
(spec/def ::dapp-transaction (spec/nilable any?))
|
||||||
|
|
||||||
(spec/def :wallet/send-transaction (allowed-keys
|
(spec/def :wallet/send-transaction (allowed-keys
|
||||||
:opt-un [::amount ::to ::to-name ::amount-error ::asset-error ::amount-text ::password
|
:opt-un [::amount ::to ::to-name ::amount-error ::asset-error ::amount-text
|
||||||
::waiting-signal? ::signing? ::id ::later?
|
::password ::show-password-input? ::id ::from ::data
|
||||||
::camera-flashlight ::in-progress?
|
::camera-flashlight ::in-progress? ::dapp-transaction
|
||||||
::wrong-password? ::from-chat? ::symbol ::advanced?
|
::wrong-password? ::from-chat? ::symbol ::advanced?
|
||||||
::gas ::gas-price ::whisper-identity ::method ::tx-hash]))
|
::gas ::gas-price ::whisper-identity ::method ::tx-hash]))
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
(ns status-im.ui.screens.wallet.send.events
|
(ns status-im.ui.screens.wallet.send.events
|
||||||
(:require [clojure.string :as string]
|
(:require [re-frame.core :as re-frame]
|
||||||
[re-frame.core :as re-frame]
|
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.native-module.core :as status]
|
[status-im.native-module.core :as status]
|
||||||
[status-im.ui.screens.db :as db]
|
|
||||||
[status-im.ui.screens.wallet.db :as wallet.db]
|
[status-im.ui.screens.wallet.db :as wallet.db]
|
||||||
[status-im.utils.ethereum.core :as ethereum]
|
[status-im.utils.ethereum.core :as ethereum]
|
||||||
[status-im.utils.ethereum.erc20 :as erc20]
|
[status-im.utils.ethereum.erc20 :as erc20]
|
||||||
[status-im.utils.ethereum.tokens :as tokens]
|
[status-im.utils.ethereum.tokens :as tokens]
|
||||||
[status-im.utils.handlers :as handlers]
|
[status-im.utils.handlers :as handlers]
|
||||||
[status-im.utils.handlers-macro :as handlers-macro]
|
[status-im.utils.handlers-macro :as handlers-macro]
|
||||||
[status-im.utils.hex :as utils.hex]
|
|
||||||
[status-im.utils.money :as money]
|
[status-im.utils.money :as money]
|
||||||
[status-im.utils.security :as security]
|
[status-im.utils.security :as security]
|
||||||
[status-im.utils.types :as types]
|
[status-im.utils.types :as types]
|
||||||
|
@ -19,71 +16,154 @@
|
||||||
[status-im.chat.models.message :as models.message]
|
[status-im.chat.models.message :as models.message]
|
||||||
[status-im.chat.commands.sending :as commands-sending]
|
[status-im.chat.commands.sending :as commands-sending]
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
[status-im.transport.utils :as transport.utils]
|
|
||||||
[taoensso.timbre :as log]
|
|
||||||
[status-im.ui.screens.navigation :as navigation]
|
[status-im.ui.screens.navigation :as navigation]
|
||||||
[status-im.wallet.transactions :as wallet.transactions]))
|
[status-im.wallet.transactions :as wallet.transactions]))
|
||||||
|
|
||||||
;;;; FX
|
;;;; FX
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(defn- send-ethers [params on-completed masked-password]
|
||||||
::accept-transaction
|
(status/send-transaction (types/clj->json params)
|
||||||
(fn [{:keys [masked-password id on-completed]}]
|
|
||||||
;; unmasking the password as late as possible to avoid being exposed from app-db
|
|
||||||
(status/approve-sign-request id
|
|
||||||
(security/unmask masked-password)
|
(security/unmask masked-password)
|
||||||
on-completed)))
|
on-completed))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(defn- send-tokens [symbol chain {:keys [from to value gas gas-price]} on-completed masked-password]
|
||||||
::accept-transaction-with-changed-gas
|
|
||||||
(fn [{:keys [masked-password id on-completed gas gas-price default-gas-price]}]
|
|
||||||
;; unmasking the password as late as possible to avoid being exposed from app-db
|
|
||||||
(if gas
|
|
||||||
(status/approve-sign-request-with-args id
|
|
||||||
(security/unmask masked-password)
|
|
||||||
(money/to-fixed gas)
|
|
||||||
(money/to-fixed (or gas-price default-gas-price))
|
|
||||||
on-completed)
|
|
||||||
(status/approve-sign-request id
|
|
||||||
(security/unmask masked-password)
|
|
||||||
on-completed))))
|
|
||||||
|
|
||||||
(defn- send-ethers [{:keys [web3 from to value gas gas-price]}]
|
|
||||||
(.sendTransaction (.-eth web3)
|
|
||||||
(clj->js {:from from :to to :value value :gas gas :gasPrice gas-price})
|
|
||||||
#()))
|
|
||||||
|
|
||||||
(defn- send-tokens [{:keys [web3 from to value gas gas-price symbol chain]}]
|
|
||||||
(let [contract (:address (tokens/symbol->token (keyword chain) symbol))]
|
(let [contract (:address (tokens/symbol->token (keyword chain) symbol))]
|
||||||
(erc20/transfer web3 contract from to value {:gas gas :gasPrice gas-price} #())))
|
(erc20/transfer contract from to value gas gas-price masked-password on-completed)))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
::send-transaction
|
::send-transaction
|
||||||
(fn [{:keys [symbol] :as params}]
|
(fn [[params symbol chain on-completed masked-password]]
|
||||||
(case symbol
|
(case symbol
|
||||||
:ETH (send-ethers params)
|
:ETH (send-ethers params on-completed masked-password)
|
||||||
(send-tokens params))))
|
(send-tokens symbol chain params on-completed masked-password))))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
(re-frame/reg-fx
|
||||||
::show-transaction-error
|
::sign-message
|
||||||
|
(fn [{:keys [params on-completed]}]
|
||||||
|
(status/sign-message (types/clj->json params)
|
||||||
|
on-completed)))
|
||||||
|
|
||||||
|
(re-frame/reg-fx
|
||||||
|
:wallet/show-transaction-error
|
||||||
(fn [message]
|
(fn [message]
|
||||||
;; (andrey) we need this timeout because modal window conflicts with alert
|
;; (andrey) we need this timeout because modal window conflicts with alert
|
||||||
(utils/set-timeout #(utils/show-popup (i18n/label :t/transaction-failed) message) 1000)))
|
(utils/set-timeout #(utils/show-popup (i18n/label :t/transaction-failed) message) 1000)))
|
||||||
|
|
||||||
(re-frame/reg-fx
|
|
||||||
:discard-transaction
|
|
||||||
(fn [id]
|
|
||||||
(status/discard-sign-request id)))
|
|
||||||
|
|
||||||
;;Helper functions
|
|
||||||
|
|
||||||
(defn transaction-valid? [{{:keys [to data]} :args}]
|
|
||||||
(or (and to (utils.hex/valid-hex? to)) (and data (not= data "0x"))))
|
|
||||||
|
|
||||||
(defn dispatch-transaction-completed [result & [modal?]]
|
|
||||||
(re-frame/dispatch [::transaction-completed {:id (:id result) :response result} modal?]))
|
|
||||||
;;;; Handlers
|
;;;; Handlers
|
||||||
|
|
||||||
|
;; SEND TRANSACTION
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:wallet/send-transaction
|
||||||
|
(fn [{{:keys [chain] :as db} :db} _]
|
||||||
|
(let [{:keys [password symbol in-progress?] :as transaction} (get-in db [:wallet :send-transaction])
|
||||||
|
from (get-in db [:account/account :address])]
|
||||||
|
(when-not in-progress?
|
||||||
|
{:db (-> db
|
||||||
|
(assoc-in [:wallet :send-transaction :wrong-password?] false)
|
||||||
|
(assoc-in [:wallet :send-transaction :in-progress?] true))
|
||||||
|
::send-transaction [(models.wallet/prepare-send-transaction from transaction)
|
||||||
|
symbol
|
||||||
|
chain
|
||||||
|
#(re-frame/dispatch [::transaction-completed (types/json->clj %)])
|
||||||
|
password]}))))
|
||||||
|
|
||||||
|
;; SIGN MESSAGE
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:wallet/sign-message
|
||||||
|
(fn [{db :db} _]
|
||||||
|
(let [{:keys [data from password]} (get-in db [:wallet :send-transaction])]
|
||||||
|
{:db (assoc-in db [:wallet :send-transaction :in-progress?] true)
|
||||||
|
::sign-message {:params {:data data
|
||||||
|
:password (security/unmask password)
|
||||||
|
:account from}
|
||||||
|
:on-completed #(re-frame/dispatch [::transaction-completed (types/json->clj %)])}})))
|
||||||
|
|
||||||
|
;; SEND TRANSACTION (SIGN MESSAGE) CALLBACK
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
::transaction-completed
|
||||||
|
(fn [{:keys [db now]} [_ {:keys [result error]}]]
|
||||||
|
(let [{:keys [id method whisper-identity to symbol amount-text dapp-transaction]} (get-in db [:wallet :send-transaction])
|
||||||
|
db' (assoc-in db [:wallet :send-transaction :in-progress?] false)]
|
||||||
|
(if error
|
||||||
|
;; ERROR
|
||||||
|
(models.wallet/handle-transaction-error db' error)
|
||||||
|
;; RESULT
|
||||||
|
(merge
|
||||||
|
{:db (cond-> (assoc-in db' [:wallet :send-transaction] {})
|
||||||
|
|
||||||
|
(not= method constants/web3-personal-sign)
|
||||||
|
(assoc-in [:wallet :transactions result]
|
||||||
|
(models.wallet/prepare-unconfirmed-transaction db now result)))}
|
||||||
|
|
||||||
|
(if dapp-transaction
|
||||||
|
(let [{:keys [message-id]} dapp-transaction
|
||||||
|
webview (:webview-bridge db)]
|
||||||
|
(models.wallet/dapp-complete-transaction (int id) result method message-id webview))
|
||||||
|
{:dispatch [:send-transaction-message whisper-identity {:address to
|
||||||
|
:asset (name symbol)
|
||||||
|
:amount amount-text
|
||||||
|
:tx-hash result}]}))))))
|
||||||
|
|
||||||
|
;; DISCARD TRANSACTION
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:wallet/discard-transaction
|
||||||
|
(fn [cofx _]
|
||||||
|
(models.wallet/discard-transaction cofx)))
|
||||||
|
|
||||||
|
;; DAPP TRANSACTIONS QUEUE
|
||||||
|
;; NOTE(andrey) We need this queue because dapp can send several transactions in a row, this is bad behaviour
|
||||||
|
;; but we need to support it
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:check-dapps-transactions-queue
|
||||||
|
(fn [{:keys [db]} _]
|
||||||
|
(let [{:keys [send-transaction transactions-queue]} (:wallet db)
|
||||||
|
{:keys [payload message-id] :as queued-transaction} (last transactions-queue)
|
||||||
|
{:keys [method params]} payload
|
||||||
|
db' (update-in db [:wallet :transactions-queue] drop-last)]
|
||||||
|
(when (and (not (:id send-transaction)) queued-transaction)
|
||||||
|
(cond
|
||||||
|
|
||||||
|
;;SEND TRANSACTION
|
||||||
|
(= method constants/web3-send-transaction)
|
||||||
|
(let [{:keys [gas gasPrice] :as transaction} (models.wallet/prepare-dapp-transaction
|
||||||
|
queued-transaction (:contacts/contacts db))
|
||||||
|
{:keys [wallet-set-up-passed?]} (:account/account db)]
|
||||||
|
{:db (assoc-in db' [:wallet :send-transaction] transaction)
|
||||||
|
:dispatch-n [[:update-wallet]
|
||||||
|
(when-not gas
|
||||||
|
[:wallet/update-estimated-gas (first params)])
|
||||||
|
(when-not gasPrice
|
||||||
|
[:wallet/update-gas-price])
|
||||||
|
[:navigate-to-modal (if wallet-set-up-passed?
|
||||||
|
:wallet-send-transaction-modal
|
||||||
|
:wallet-onboarding-setup-modal)]]})
|
||||||
|
|
||||||
|
;;SIGN MESSAGE
|
||||||
|
(= method constants/web3-personal-sign)
|
||||||
|
(let [[address data] (models.wallet/normalize-sign-message-params params)]
|
||||||
|
(if (and address data)
|
||||||
|
{:db (assoc-in db' [:wallet :send-transaction] {:id (str message-id)
|
||||||
|
:from address
|
||||||
|
:data data
|
||||||
|
:dapp-transaction queued-transaction
|
||||||
|
:method method})
|
||||||
|
:dispatch [:navigate-to-modal :wallet-sign-message-modal]}
|
||||||
|
{:db db'})))))))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:send-transaction-message
|
||||||
|
(concat models.message/send-interceptors
|
||||||
|
navigation/navigation-interceptors)
|
||||||
|
(fn [{:keys [db] :as cofx} [chat-id params]]
|
||||||
|
;;NOTE(goranjovic): we want to send the payment message only when we have a whisper id
|
||||||
|
;; for the recipient, we always redirect to `:wallet-transaction-sent` even when we don't
|
||||||
|
(if-let [send-command (and chat-id (get-in db [:id->command ["send" #{:personal-chats}]]))]
|
||||||
|
(handlers-macro/merge-fx cofx
|
||||||
|
(commands-sending/send chat-id send-command params)
|
||||||
|
(navigation/replace-view :wallet-transaction-sent))
|
||||||
|
(handlers-macro/merge-fx cofx
|
||||||
|
(navigation/replace-view :wallet-transaction-sent)))))
|
||||||
|
|
||||||
(defn set-and-validate-amount-db [db amount symbol decimals]
|
(defn set-and-validate-amount-db [db amount symbol decimals]
|
||||||
(let [{:keys [value error]} (wallet.db/parse-amount amount decimals)]
|
(let [{:keys [value error]} (wallet.db/parse-amount amount decimals)]
|
||||||
(-> db
|
(-> db
|
||||||
|
@ -96,6 +176,13 @@
|
||||||
(fn [{:keys [db]} [_ amount symbol decimals]]
|
(fn [{:keys [db]} [_ amount symbol decimals]]
|
||||||
{:db (set-and-validate-amount-db db amount symbol decimals)}))
|
{:db (set-and-validate-amount-db db amount symbol decimals)}))
|
||||||
|
|
||||||
|
(handlers/register-handler-fx
|
||||||
|
:wallet/discard-transaction-navigate-back
|
||||||
|
(fn [cofx _]
|
||||||
|
(-> cofx
|
||||||
|
models.wallet/discard-transaction
|
||||||
|
(assoc :dispatch [:navigate-back]))))
|
||||||
|
|
||||||
(defn update-gas-price
|
(defn update-gas-price
|
||||||
([db edit? success-event]
|
([db edit? success-event]
|
||||||
{:update-gas-price {:web3 (:web3 db)
|
{:update-gas-price {:web3 (:web3 db)
|
||||||
|
@ -130,305 +217,11 @@
|
||||||
(fn [{:keys [db]} [_ advanced?]]
|
(fn [{:keys [db]} [_ advanced?]]
|
||||||
{:db (assoc-in db [:wallet :send-transaction :advanced?] advanced?)}))
|
{:db (assoc-in db [:wallet :send-transaction :advanced?] advanced?)}))
|
||||||
|
|
||||||
(def ^:private clear-send-properties {:id nil
|
|
||||||
:signing? false
|
|
||||||
:wrong-password? false
|
|
||||||
:waiting-signal? false
|
|
||||||
:from-chat? false
|
|
||||||
:in-progress? false
|
|
||||||
:password nil})
|
|
||||||
|
|
||||||
;; TODO(goranjovic) - this is a temporary workaround because in regular `clear-send-properties` we need `:from-chat?`
|
|
||||||
;; to be false to avoid the wallet onboarding crash if the user accessed transactions from dapps first.
|
|
||||||
;; On the other hand, if we reset the same flag to false every time we clear the current transaction that would also
|
|
||||||
;; happen when user clicks Cancel in chat initiated transaction and then all fields would become editable, which is
|
|
||||||
;; another bug.
|
|
||||||
;; This temporary workaround resets all fields except the flag if we are in a chat /send transaction.
|
|
||||||
;; A permanent solution would be to add onboarding check to dapp transaction and/or review the workflow.
|
|
||||||
(def ^:private partial-clear-send-properties {:id nil
|
|
||||||
:signing? false
|
|
||||||
:wrong-password? false
|
|
||||||
:waiting-signal? false
|
|
||||||
:in-progress? false
|
|
||||||
:password nil})
|
|
||||||
|
|
||||||
(defn on-transactions-completed [raw-results]
|
|
||||||
(let [result (types/json->clj raw-results)]
|
|
||||||
(dispatch-transaction-completed result)))
|
|
||||||
|
|
||||||
(defn prepare-transaction [{:keys [id message_id method args]} now]
|
|
||||||
;;NOTE(goranjovic): the transactions started from chat using /send command
|
|
||||||
;; are only in ether, so this parameter defaults to ETH
|
|
||||||
(let [{:keys [from to value symbol data gas gasPrice] :or {symbol :ETH}} args]
|
|
||||||
{:id id
|
|
||||||
:from from
|
|
||||||
:to to
|
|
||||||
:to-name (when (nil? to)
|
|
||||||
(i18n/label :t/new-contract))
|
|
||||||
:symbol symbol
|
|
||||||
:method method
|
|
||||||
:value (money/bignumber (or value 0))
|
|
||||||
:data data
|
|
||||||
:gas (when (seq gas)
|
|
||||||
(money/bignumber (money/to-decimal gas)))
|
|
||||||
:gas-price (when (seq gasPrice)
|
|
||||||
(money/bignumber (money/to-decimal gasPrice)))
|
|
||||||
:timestamp now
|
|
||||||
:message-id message_id}))
|
|
||||||
|
|
||||||
;;TRANSACTION QUEUED signal from status-go
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:sign-request-queued
|
:wallet/cancel-entering-password
|
||||||
(fn [{:keys [db]} [_ transaction]]
|
|
||||||
{:db (update-in db [:wallet :transactions-queue] conj transaction)
|
|
||||||
:dispatch [:check-transactions-queue]}))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:check-transactions-queue
|
|
||||||
(fn [{:keys [db now]} _]
|
|
||||||
(let [{:keys [send-transaction transactions-queue]} (:wallet db)
|
|
||||||
{:keys [id method args] :as queued-transaction} (last transactions-queue)
|
|
||||||
db' (update-in db [:wallet :transactions-queue] drop-last)]
|
|
||||||
(when (and (not (:id send-transaction)) queued-transaction)
|
|
||||||
(cond
|
|
||||||
;;SEND TRANSACTION
|
|
||||||
(= method constants/web3-send-transaction)
|
|
||||||
|
|
||||||
(let [{:keys [gas gasPrice]} args
|
|
||||||
transaction (prepare-transaction queued-transaction now)
|
|
||||||
sending-from-dapp? (not (get-in db [:wallet :send-transaction :waiting-signal?]))
|
|
||||||
new-db (assoc-in db' [:wallet :transactions-unsigned id] transaction)
|
|
||||||
sender-account (:account/account db)
|
|
||||||
sending-db {:id id
|
|
||||||
:method method
|
|
||||||
:from-chat? (or sending-from-dapp? ;;TODO(goranjovic): figure out why we need to
|
|
||||||
;; have from-chat? flag for dapp txs and get rid of this
|
|
||||||
(get-in db [:wallet :send-transaction :from-chat?]))}]
|
|
||||||
(if sending-from-dapp?
|
|
||||||
;;SENDING FROM DAPP
|
|
||||||
{:db (assoc-in new-db [:wallet :send-transaction] sending-db) ; we need to completely reset sending state here
|
|
||||||
:dispatch-n [[:update-wallet]
|
|
||||||
[:navigate-to-modal (if (:wallet-set-up-passed? sender-account)
|
|
||||||
:wallet-send-transaction-modal
|
|
||||||
:wallet-onboarding-setup-modal)]
|
|
||||||
(when-not (seq gas)
|
|
||||||
[:wallet/update-estimated-gas transaction])
|
|
||||||
(when-not (seq gasPrice)
|
|
||||||
[:wallet/update-gas-price])]}
|
|
||||||
;;WALLET SEND SCREEN WAITING SIGNAL
|
|
||||||
(let [{:keys [password]} (get-in db [:wallet :send-transaction])
|
|
||||||
new-db' (update-in new-db [:wallet :send-transaction] merge sending-db)] ; just update sending state as we are in wallet flow
|
|
||||||
{:db new-db'
|
|
||||||
::accept-transaction {:id id
|
|
||||||
:masked-password password
|
|
||||||
:on-completed on-transactions-completed}})))
|
|
||||||
;;SIGN MESSAGE
|
|
||||||
(= method constants/web3-personal-sign)
|
|
||||||
|
|
||||||
(let [{:keys [data]} args
|
|
||||||
data' (transport.utils/to-utf8 data)]
|
|
||||||
(if data'
|
|
||||||
{:db (-> db'
|
|
||||||
(assoc-in [:wallet :transactions-unsigned id] {:data data' :id id})
|
|
||||||
(assoc-in [:wallet :send-transaction] {:id id :method method}))
|
|
||||||
:dispatch [:navigate-to-modal :wallet-sign-message-modal]}
|
|
||||||
{:db db'})))))))
|
|
||||||
|
|
||||||
(defn this-transaction-signing? [id signing-id view-id modal]
|
|
||||||
(and (= signing-id id)
|
|
||||||
(or (= view-id :wallet-send-transaction)
|
|
||||||
(= view-id :wallet-send-transaction-chat)
|
|
||||||
(= modal :wallet-send-transaction-modal)
|
|
||||||
(= modal :wallet-sign-message-modal))))
|
|
||||||
|
|
||||||
(defn handle-failed-tx [cofx error_message]
|
|
||||||
(-> cofx
|
|
||||||
(assoc ::show-transaction-error error_message)
|
|
||||||
(update-in [:db :wallet] dissoc :send-transaction)))
|
|
||||||
|
|
||||||
;;TRANSACTION FAILED signal from status-go
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:sign-request-failed
|
|
||||||
(fn [{{:keys [view-id modal] :as db} :db} [_ {:keys [id method error_code error_message]}]]
|
|
||||||
(let [send-transaction (get-in db [:wallet :send-transaction])]
|
|
||||||
(case error_code
|
|
||||||
|
|
||||||
;;WRONG PASSWORD
|
|
||||||
constants/send-transaction-password-error-code
|
|
||||||
{:db (-> db
|
|
||||||
(assoc-in [:wallet :send-transaction :wrong-password?] true)
|
|
||||||
(assoc-in [:wallet :send-transaction :waiting-signal?] false))}
|
|
||||||
|
|
||||||
;;NO ERROR, DISCARDED, TIMEOUT or DEFAULT ERROR
|
|
||||||
(if (this-transaction-signing? id (:id send-transaction) view-id modal)
|
|
||||||
(cond-> {:db (-> db
|
|
||||||
navigation/navigate-back
|
|
||||||
(assoc-in [:wallet :transactions-queue] nil)
|
|
||||||
(update-in [:wallet :transactions-unsigned] dissoc id)
|
|
||||||
(update-in [:wallet :send-transaction] merge clear-send-properties))}
|
|
||||||
(= method constants/web3-send-transaction)
|
|
||||||
(handle-failed-tx error_message))
|
|
||||||
{:db (update-in db [:wallet :transactions-unsigned] dissoc id)})))))
|
|
||||||
|
|
||||||
(defn prepare-unconfirmed-dapp-transaction [now hash transaction]
|
|
||||||
(-> transaction
|
|
||||||
(assoc :confirmations "0"
|
|
||||||
:timestamp (str now)
|
|
||||||
:type :outbound
|
|
||||||
:hash hash)
|
|
||||||
(update :gas-price str)
|
|
||||||
(update :value str)
|
|
||||||
(update :gas str)
|
|
||||||
(dissoc :message-id :id)))
|
|
||||||
|
|
||||||
(defn prepare-unconfirmed-status-transaction [db now hash transaction]
|
|
||||||
(let [chain (:chain db)
|
|
||||||
token (tokens/symbol->token (keyword chain) (:symbol transaction))]
|
|
||||||
(-> transaction
|
|
||||||
(assoc :confirmations "0"
|
|
||||||
:timestamp (str now)
|
|
||||||
:type :outbound
|
|
||||||
:hash hash)
|
|
||||||
(update :gas-price str)
|
|
||||||
(assoc :value (:amount transaction))
|
|
||||||
(assoc :token token)
|
|
||||||
(update :gas str)
|
|
||||||
(dissoc :message-id :id))))
|
|
||||||
|
|
||||||
(defn prepare-unconfirmed-transaction [db now hash id]
|
|
||||||
(let [unsigned-transaction (get-in db [:wallet :transactions-unsigned id])
|
|
||||||
send-transaction (get-in db [:wallet :send-transaction])]
|
|
||||||
;;TODO(goranjovic) - unify `send-transaction` with transactions-unsigned`
|
|
||||||
;; currently the latter is only used for transactions initiated from dapps
|
|
||||||
(if-not (:symbol send-transaction)
|
|
||||||
(prepare-unconfirmed-dapp-transaction now hash unsigned-transaction)
|
|
||||||
(prepare-unconfirmed-status-transaction db now hash send-transaction))))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:send-transaction-message
|
|
||||||
(concat models.message/send-interceptors
|
|
||||||
navigation/navigation-interceptors)
|
|
||||||
(fn [{:keys [db] :as cofx} [chat-id params]]
|
|
||||||
;;NOTE(goranjovic): we want to send the payment message only when we have a whisper id
|
|
||||||
;; for the recipient, we always redirect to `:wallet-transaction-sent` even when we don't
|
|
||||||
(if-let [send-command (and chat-id (get-in db [:id->command ["send" #{:personal-chats}]]))]
|
|
||||||
(handlers-macro/merge-fx cofx
|
|
||||||
(commands-sending/send chat-id send-command params)
|
|
||||||
(navigation/replace-view :wallet-transaction-sent))
|
|
||||||
(handlers-macro/merge-fx cofx
|
|
||||||
(navigation/replace-view :wallet-transaction-sent)))))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
::transaction-completed
|
|
||||||
(fn [{db :db now :now} [_ {:keys [id response] :as params} modal?]]
|
|
||||||
(let [{:keys [hash error]} response
|
|
||||||
{:keys [method whisper-identity to symbol amount-text]} (get-in db [:wallet :send-transaction])
|
|
||||||
db' (assoc-in db [:wallet :send-transaction :in-progress?] false)]
|
|
||||||
(if (and error (string? error) (not (string/blank? error))) ;; ignore error here, error will be handled in :transaction-failed
|
|
||||||
{:db db'}
|
|
||||||
(merge
|
|
||||||
{:db (cond-> db'
|
|
||||||
(= method constants/web3-send-transaction)
|
|
||||||
(assoc-in [:wallet :transactions hash] (prepare-unconfirmed-transaction db now hash id))
|
|
||||||
true
|
|
||||||
(update-in [:wallet :transactions-unsigned] dissoc id)
|
|
||||||
true
|
|
||||||
(update-in [:wallet :send-transaction] merge clear-send-properties {:tx-hash hash}))}
|
|
||||||
(if modal?
|
|
||||||
|
|
||||||
(cond-> {:dispatch [:navigate-back]}
|
|
||||||
(= method constants/web3-send-transaction)
|
|
||||||
(assoc :dispatch-later [{:ms 400 :dispatch [:navigate-to-modal :wallet-transaction-sent-modal]}]))
|
|
||||||
|
|
||||||
{:dispatch [:send-transaction-message whisper-identity {:address to
|
|
||||||
:asset (name symbol)
|
|
||||||
:amount amount-text
|
|
||||||
:tx-hash hash}]}))))))
|
|
||||||
|
|
||||||
(defn on-transactions-modal-completed [raw-results]
|
|
||||||
(let [result (types/json->clj raw-results)]
|
|
||||||
(dispatch-transaction-completed result true)))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:wallet/sign-transaction
|
|
||||||
(fn [{{:keys [web3 chain] :as db} :db} [_ later?]]
|
|
||||||
(let [db' (assoc-in db [:wallet :send-transaction :wrong-password?] false)
|
|
||||||
{:keys [amount id password to symbol method gas gas-price]} (get-in db [:wallet :send-transaction])]
|
|
||||||
(if id
|
|
||||||
{::accept-transaction {:id id
|
|
||||||
:masked-password password
|
|
||||||
:on-completed on-transactions-completed}
|
|
||||||
:db (assoc-in db' [:wallet :send-transaction :in-progress?] true)}
|
|
||||||
{:db (update-in db' [:wallet :send-transaction] assoc
|
|
||||||
:waiting-signal? true
|
|
||||||
:later? later?
|
|
||||||
:in-progress? true)
|
|
||||||
::send-transaction {:web3 web3
|
|
||||||
:from (get-in db [:account/account :address])
|
|
||||||
:to to
|
|
||||||
:value amount
|
|
||||||
:gas gas
|
|
||||||
:gas-price gas-price
|
|
||||||
:symbol symbol
|
|
||||||
:method method
|
|
||||||
:chain chain}}))))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:wallet/sign-message-modal
|
|
||||||
(fn [{db :db} _]
|
|
||||||
(let [{:keys [id password]} (get-in db [:wallet :send-transaction])]
|
|
||||||
{:db (assoc-in db [:wallet :send-transaction :in-progress?] true)
|
|
||||||
::accept-transaction {:id id
|
|
||||||
:masked-password password
|
|
||||||
:on-completed on-transactions-modal-completed}})))
|
|
||||||
|
|
||||||
(defn sign-transaction-modal [{:keys [db]} default-gas-price]
|
|
||||||
;;TODO(goranjovic) - unify send-transaction and unsigned-transaction
|
|
||||||
(let [{:keys [id password] :as send-transaction} (get-in db [:wallet :send-transaction])
|
|
||||||
{:keys [gas gas-price]} [:wallet.send/unsigned-transaction]]
|
|
||||||
{:db (assoc-in db [:wallet :send-transaction :in-progress?] true)
|
|
||||||
::accept-transaction-with-changed-gas {:id id
|
|
||||||
:masked-password password
|
|
||||||
:gas (or gas (:gas send-transaction))
|
|
||||||
:gas-price (or gas-price (:gas-price send-transaction))
|
|
||||||
:default-gas-price default-gas-price
|
|
||||||
:on-completed on-transactions-modal-completed}}))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:wallet/sign-transaction-modal-update-gas-success
|
|
||||||
(fn [cofx [_ default-gas-price]]
|
|
||||||
(sign-transaction-modal cofx default-gas-price)))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:wallet/sign-transaction-modal
|
|
||||||
(fn [{:keys [db]} _]
|
|
||||||
(update-gas-price db false :wallet/sign-transaction-modal-update-gas-success)))
|
|
||||||
|
|
||||||
(defn discard-transaction
|
|
||||||
[{:keys [db]}]
|
|
||||||
(let [{:keys [id from-chat?]} (get-in db [:wallet :send-transaction])
|
|
||||||
clear-fields (if from-chat? partial-clear-send-properties clear-send-properties)]
|
|
||||||
(merge {:db (update-in db [:wallet :send-transaction] merge clear-fields)}
|
|
||||||
(when id
|
|
||||||
{:discard-transaction id}))))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:wallet/discard-transaction
|
|
||||||
(fn [cofx _]
|
|
||||||
(discard-transaction cofx)))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:wallet/discard-transaction-navigate-back
|
|
||||||
(fn [cofx _]
|
|
||||||
(-> cofx
|
|
||||||
discard-transaction
|
|
||||||
(assoc :dispatch [:navigate-back]))))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:wallet/cancel-signing-modal
|
|
||||||
(fn [{:keys [db]} _]
|
(fn [{:keys [db]} _]
|
||||||
{:db (update-in db [:wallet :send-transaction] assoc
|
{:db (update-in db [:wallet :send-transaction] assoc
|
||||||
:signing? false
|
:show-password-input? false
|
||||||
:wrong-password? false
|
:wrong-password? false
|
||||||
:password nil)}))
|
:password nil)}))
|
||||||
|
|
||||||
|
@ -437,11 +230,6 @@
|
||||||
(fn [{:keys [db]} [_ masked-password]]
|
(fn [{:keys [db]} [_ masked-password]]
|
||||||
{:db (assoc-in db [:wallet :send-transaction :password] masked-password)}))
|
{:db (assoc-in db [:wallet :send-transaction :password] masked-password)}))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
|
||||||
:wallet.send/set-signing?
|
|
||||||
(fn [{:keys [db]} [_ signing?]]
|
|
||||||
{:db (assoc-in db [:wallet :send-transaction :signing?] signing?)}))
|
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:wallet.send/edit-value
|
:wallet.send/edit-value
|
||||||
(fn [cofx [_ key value]]
|
(fn [cofx [_ key value]]
|
||||||
|
@ -475,7 +263,7 @@
|
||||||
:close-transaction-sent-screen
|
:close-transaction-sent-screen
|
||||||
(fn [{:keys [db]} [_ chat-id]]
|
(fn [{:keys [db]} [_ chat-id]]
|
||||||
{:dispatch [:navigate-back]
|
{:dispatch [:navigate-back]
|
||||||
:dispatch-later [{:ms 400 :dispatch [:check-transactions-queue]}]}))
|
:dispatch-later [{:ms 400 :dispatch [:check-dapps-transactions-queue]}]}))
|
||||||
|
|
||||||
(handlers/register-handler-fx
|
(handlers/register-handler-fx
|
||||||
:sync-wallet-transactions
|
:sync-wallet-transactions
|
||||||
|
|
|
@ -1,55 +1,44 @@
|
||||||
(ns status-im.ui.screens.wallet.send.subs
|
(ns status-im.ui.screens.wallet.send.subs
|
||||||
(:require [re-frame.core :as re-frame]
|
(:require [re-frame.core :as re-frame]
|
||||||
[status-im.utils.money :as money]
|
[status-im.utils.money :as money]
|
||||||
[status-im.models.wallet :as models.wallet]
|
[status-im.models.wallet :as models.wallet]))
|
||||||
[status-im.utils.hex :as utils.hex]))
|
|
||||||
|
|
||||||
(re-frame/reg-sub ::send-transaction
|
(re-frame/reg-sub
|
||||||
|
::send-transaction
|
||||||
:<- [:wallet]
|
:<- [:wallet]
|
||||||
(fn [wallet]
|
(fn [wallet]
|
||||||
(:send-transaction wallet)))
|
(:send-transaction wallet)))
|
||||||
|
|
||||||
(re-frame/reg-sub :wallet.send/symbol
|
(re-frame/reg-sub
|
||||||
|
:wallet.send/symbol
|
||||||
:<- [::send-transaction]
|
:<- [::send-transaction]
|
||||||
(fn [send-transaction]
|
(fn [send-transaction]
|
||||||
(:symbol send-transaction)))
|
(:symbol send-transaction)))
|
||||||
|
|
||||||
(re-frame/reg-sub :wallet.send/advanced?
|
(re-frame/reg-sub
|
||||||
|
:wallet.send/advanced?
|
||||||
:<- [::send-transaction]
|
:<- [::send-transaction]
|
||||||
(fn [send-transaction]
|
(fn [send-transaction]
|
||||||
(:advanced? send-transaction)))
|
(:advanced? send-transaction)))
|
||||||
|
|
||||||
(re-frame/reg-sub :wallet.send/camera-flashlight
|
(re-frame/reg-sub
|
||||||
|
:wallet.send/camera-flashlight
|
||||||
:<- [::send-transaction]
|
:<- [::send-transaction]
|
||||||
(fn [send-transaction]
|
(fn [send-transaction]
|
||||||
(:camera-flashlight send-transaction)))
|
(:camera-flashlight send-transaction)))
|
||||||
|
|
||||||
(re-frame/reg-sub :wallet.send/wrong-password?
|
(re-frame/reg-sub
|
||||||
|
:wallet.send/wrong-password?
|
||||||
:<- [::send-transaction]
|
:<- [::send-transaction]
|
||||||
(fn [send-transaction]
|
(fn [send-transaction]
|
||||||
(:wrong-password? send-transaction)))
|
(:wrong-password? send-transaction)))
|
||||||
|
|
||||||
(re-frame/reg-sub :wallet.send/sign-password-enabled?
|
(re-frame/reg-sub
|
||||||
|
:wallet.send/sign-password-enabled?
|
||||||
:<- [::send-transaction]
|
:<- [::send-transaction]
|
||||||
(fn [{:keys [password]}]
|
(fn [{:keys [password]}]
|
||||||
(and (not (nil? password)) (not= password ""))))
|
(and (not (nil? password)) (not= password ""))))
|
||||||
|
|
||||||
(re-frame/reg-sub ::unsigned-transactions
|
|
||||||
:<- [:wallet]
|
|
||||||
(fn [wallet]
|
|
||||||
(:transactions-unsigned wallet)))
|
|
||||||
|
|
||||||
(re-frame/reg-sub ::unsigned-transaction
|
|
||||||
:<- [::send-transaction]
|
|
||||||
:<- [::unsigned-transactions]
|
|
||||||
(fn [[send-transaction unsigned-transactions]]
|
|
||||||
(when-let [unsigned-transaction (get unsigned-transactions
|
|
||||||
(:id send-transaction))]
|
|
||||||
(merge send-transaction
|
|
||||||
unsigned-transaction
|
|
||||||
{:gas (or (:gas send-transaction) (:gas unsigned-transaction))
|
|
||||||
:gas-price (or (:gas-price send-transaction) (:gas-price unsigned-transaction))}))))
|
|
||||||
|
|
||||||
(defn edit-or-transaction-data
|
(defn edit-or-transaction-data
|
||||||
"Set up edit data structure, defaulting to transaction when not available"
|
"Set up edit data structure, defaulting to transaction when not available"
|
||||||
[transaction edit]
|
[transaction edit]
|
||||||
|
@ -64,15 +53,13 @@
|
||||||
:gas
|
:gas
|
||||||
(money/to-fixed (:gas transaction)))))
|
(money/to-fixed (:gas transaction)))))
|
||||||
|
|
||||||
(re-frame/reg-sub :wallet/edit
|
(re-frame/reg-sub
|
||||||
|
:wallet/edit
|
||||||
:<- [::send-transaction]
|
:<- [::send-transaction]
|
||||||
:<- [::unsigned-transaction]
|
|
||||||
:<- [:wallet]
|
:<- [:wallet]
|
||||||
(fn [[send-transaction unsigned-transaction {:keys [edit]}]]
|
(fn [[send-transaction {:keys [edit]}]]
|
||||||
(edit-or-transaction-data
|
(edit-or-transaction-data
|
||||||
(if (:id send-transaction)
|
send-transaction
|
||||||
unsigned-transaction
|
|
||||||
send-transaction)
|
|
||||||
edit)))
|
edit)))
|
||||||
|
|
||||||
(defn check-sufficient-funds [transaction balance symbol amount]
|
(defn check-sufficient-funds [transaction balance symbol amount]
|
||||||
|
@ -93,25 +80,13 @@
|
||||||
(money/formatted->internal :ETH 18))
|
(money/formatted->internal :ETH 18))
|
||||||
(money/bignumber available-for-gas))))))
|
(money/bignumber available-for-gas))))))
|
||||||
|
|
||||||
(re-frame/reg-sub :wallet.send/transaction
|
(re-frame/reg-sub
|
||||||
|
:wallet.send/transaction
|
||||||
:<- [::send-transaction]
|
:<- [::send-transaction]
|
||||||
:<- [:balance]
|
:<- [:balance]
|
||||||
(fn [[{:keys [amount symbol] :as transaction} balance]]
|
(fn [[{:keys [amount symbol] :as transaction} balance]]
|
||||||
(-> transaction
|
(-> transaction
|
||||||
|
(models.wallet/transform-data-for-message)
|
||||||
(models.wallet/add-max-fee)
|
(models.wallet/add-max-fee)
|
||||||
(check-sufficient-funds balance symbol amount)
|
(check-sufficient-funds balance symbol amount)
|
||||||
(check-sufficient-gas 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))))))
|
|
||||||
|
|
|
@ -23,9 +23,45 @@
|
||||||
[status-im.utils.security :as security]
|
[status-im.utils.security :as security]
|
||||||
[status-im.utils.utils :as utils]
|
[status-im.utils.utils :as utils]
|
||||||
[status-im.utils.ethereum.tokens :as tokens]
|
[status-im.utils.ethereum.tokens :as tokens]
|
||||||
[status-im.utils.ethereum.core :as ethereum]))
|
[status-im.utils.ethereum.core :as ethereum]
|
||||||
|
[status-im.transport.utils :as transport.utils]))
|
||||||
|
|
||||||
(defview sign-panel [message-label spinning?]
|
(defn- toolbar [modal? title]
|
||||||
|
(let [action (if modal? act/close-white act/back-white)]
|
||||||
|
[toolbar/toolbar {:style wallet.styles/toolbar}
|
||||||
|
[toolbar/nav-button (action (if modal?
|
||||||
|
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
|
||||||
|
#(act/default-handler)))]
|
||||||
|
[toolbar/content-title {:color :white} title]]))
|
||||||
|
|
||||||
|
(defn- advanced-cartouche [{:keys [max-fee gas gas-price]}]
|
||||||
|
[react/view
|
||||||
|
[wallet.components/cartouche {:on-press #(do (re-frame/dispatch [:wallet.send/clear-gas])
|
||||||
|
(re-frame/dispatch [:navigate-to-modal :wallet-transaction-fee]))}
|
||||||
|
(i18n/label :t/wallet-transaction-fee)
|
||||||
|
[react/view {:style styles/advanced-options-text-wrapper
|
||||||
|
:accessibility-label :transaction-fee-button}
|
||||||
|
[react/text {:style styles/advanced-fees-text}
|
||||||
|
(str max-fee " " (i18n/label :t/eth))]
|
||||||
|
[react/text {:style styles/advanced-fees-details-text}
|
||||||
|
(str (money/to-fixed gas) " * " (money/to-fixed (money/wei-> :gwei gas-price)) (i18n/label :t/gwei))]]]])
|
||||||
|
|
||||||
|
(defn- advanced-options [advanced? transaction scroll]
|
||||||
|
[react/view {:style styles/advanced-wrapper}
|
||||||
|
[react/touchable-highlight {:on-press (fn []
|
||||||
|
(re-frame/dispatch [:wallet.send/toggle-advanced (not advanced?)])
|
||||||
|
(when (and scroll @scroll) (utils/set-timeout #(.scrollToEnd @scroll) 350)))}
|
||||||
|
[react/view {:style styles/advanced-button-wrapper}
|
||||||
|
[react/view {:style styles/advanced-button
|
||||||
|
:accessibility-label :advanced-button}
|
||||||
|
[react/i18n-text {:style (merge wallet.components.styles/label
|
||||||
|
styles/advanced-label)
|
||||||
|
:key :wallet-advanced}]
|
||||||
|
[vector-icons/icon (if advanced? :icons/up :icons/down) {:color :white}]]]]
|
||||||
|
(when advanced?
|
||||||
|
[advanced-cartouche transaction])])
|
||||||
|
|
||||||
|
(defview password-input-panel [message-label spinning?]
|
||||||
(letsubs [account [:get-current-account]
|
(letsubs [account [:get-current-account]
|
||||||
wrong-password? [:wallet.send/wrong-password?]
|
wrong-password? [:wallet.send/wrong-password?]
|
||||||
signing-phrase (:signing-phrase @account)
|
signing-phrase (:signing-phrase @account)
|
||||||
|
@ -37,8 +73,6 @@
|
||||||
[tooltip/tooltip (i18n/label :t/wrong-password) styles/password-error-tooltip])
|
[tooltip/tooltip (i18n/label :t/wrong-password) styles/password-error-tooltip])
|
||||||
[react/animated-view {:style (styles/sign-panel opacity-value)}
|
[react/animated-view {:style (styles/sign-panel opacity-value)}
|
||||||
[react/view styles/spinner-container
|
[react/view styles/spinner-container
|
||||||
;;NOTE(goranjovic) - android build doesn't seem to react on change in `:animating` property, so
|
|
||||||
;;we have this workaround of just using `when` around the whole element.
|
|
||||||
(when spinning?
|
(when spinning?
|
||||||
[react/activity-indicator {:animating true
|
[react/activity-indicator {:animating true
|
||||||
:size :large}])]
|
:size :large}])]
|
||||||
|
@ -58,8 +92,8 @@
|
||||||
:accessibility-label :enter-password-input
|
:accessibility-label :enter-password-input
|
||||||
:auto-capitalize :none}]]]]))
|
:auto-capitalize :none}]]]]))
|
||||||
|
|
||||||
;; "Cancel" and "Sign Transaction >" buttons, signing with password
|
;; "Cancel" and "Sign Transaction >" or "Sign >" buttons, signing with password
|
||||||
(defview signing-buttons [spinning? cancel-handler sign-handler sign-label]
|
(defview enter-password-buttons [spinning? cancel-handler sign-handler sign-label]
|
||||||
(letsubs [sign-enabled? [:wallet.send/sign-password-enabled?]]
|
(letsubs [sign-enabled? [:wallet.send/sign-password-enabled?]]
|
||||||
[bottom-buttons/bottom-buttons
|
[bottom-buttons/bottom-buttons
|
||||||
styles/sign-buttons
|
styles/sign-buttons
|
||||||
|
@ -74,156 +108,34 @@
|
||||||
(i18n/label sign-label)
|
(i18n/label sign-label)
|
||||||
[vector-icons/icon :icons/forward {:color :white}]]]))
|
[vector-icons/icon :icons/forward {:color :white}]]]))
|
||||||
|
|
||||||
(defn- sign-enabled? [amount-error to amount modal?]
|
;; "Sign Transaction >" button
|
||||||
(and
|
(defn- sign-transaction-button [amount-error to amount sufficient-funds? sufficient-gas? modal?]
|
||||||
(nil? amount-error)
|
(let [sign-enabled? (and (nil? amount-error)
|
||||||
(or modal? (not (empty? to))) ;;NOTE(goranjovic) - contract creation will have empty `to`
|
(or modal? (not (empty? to))) ;;NOTE(goranjovic) - contract creation will have empty `to`
|
||||||
(not (nil? amount))))
|
(not (nil? amount))
|
||||||
|
sufficient-funds?
|
||||||
;; "Sign Later" and "Sign Transaction >" buttons
|
sufficient-gas?)]
|
||||||
(defn- sign-button [amount-error to amount sufficient-funds? sufficient-gas? modal?]
|
|
||||||
(let [sign-enabled? (sign-enabled? amount-error to amount modal?)
|
|
||||||
immediate-sign-enabled? (and sign-enabled? sufficient-funds? sufficient-gas?)]
|
|
||||||
[bottom-buttons/bottom-buttons
|
[bottom-buttons/bottom-buttons
|
||||||
styles/sign-buttons
|
styles/sign-buttons
|
||||||
[react/view]
|
[react/view]
|
||||||
[button/button {:style components.styles/flex
|
[button/button {:style components.styles/flex
|
||||||
:disabled? (not immediate-sign-enabled?)
|
:disabled? (not sign-enabled?)
|
||||||
:on-press #(re-frame/dispatch [:wallet.send/set-signing? true])
|
:on-press #(re-frame/dispatch [:set-in
|
||||||
|
[:wallet :send-transaction :show-password-input?]
|
||||||
|
true])
|
||||||
:text-style {:color :white}
|
:text-style {:color :white}
|
||||||
:accessibility-label :sign-transaction-button}
|
:accessibility-label :sign-transaction-button}
|
||||||
(i18n/label :t/transactions-sign-transaction)
|
(i18n/label :t/transactions-sign-transaction)
|
||||||
[vector-icons/icon :icons/forward {:color (if immediate-sign-enabled? :white :gray)}]]]))
|
[vector-icons/icon :icons/forward {:color (if sign-enabled? :white :gray)}]]]))
|
||||||
|
|
||||||
(defn return-to-transaction [dapp-transaction?]
|
;; MAIN SEND TRANSACTION VIEW
|
||||||
(if dapp-transaction?
|
(defn- send-transaction-view [{:keys [modal? transaction scroll advanced? network]}]
|
||||||
(re-frame/dispatch [:navigate-to-modal :wallet-send-transaction-modal])
|
(let [{:keys [amount amount-text amount-error asset-error show-password-input? to to-name sufficient-funds?
|
||||||
(act/default-handler)))
|
sufficient-gas? in-progress? from-chat? symbol]} transaction
|
||||||
|
{:keys [decimals] :as token} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)]
|
||||||
(defn handler [discard? dapp-transaction?]
|
|
||||||
(if discard?
|
|
||||||
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
|
|
||||||
#(return-to-transaction dapp-transaction?)))
|
|
||||||
|
|
||||||
(defn- toolbar [discard? dapp-transaction? action title]
|
|
||||||
[toolbar/toolbar {:style wallet.styles/toolbar}
|
|
||||||
[toolbar/nav-button (action (handler discard? dapp-transaction?))]
|
|
||||||
[toolbar/content-title {:color :white} title]])
|
|
||||||
|
|
||||||
(defview transaction-fee []
|
|
||||||
(letsubs [send-transaction [:wallet.send/transaction]
|
|
||||||
unsigned-transaction [:wallet.send/unsigned-transaction]
|
|
||||||
network [:get-current-account-network]
|
|
||||||
{gas-edit :gas
|
|
||||||
max-fee :max-fee
|
|
||||||
gas-price-edit :gas-price} [:wallet/edit]]
|
|
||||||
(let [modal? (:id send-transaction)
|
|
||||||
;;TODO(goranjovic) - unify unsigned and regular transaction subs
|
|
||||||
{:keys [amount symbol] :as transaction} (if modal? unsigned-transaction send-transaction)
|
|
||||||
gas (:value gas-edit)
|
|
||||||
gas-price (:value gas-price-edit)
|
|
||||||
{:keys [decimals]} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)]
|
|
||||||
[wallet.components/simple-screen {:status-bar-type :modal-wallet}
|
|
||||||
[toolbar false modal? act/close-white
|
|
||||||
(i18n/label :t/wallet-transaction-fee)]
|
|
||||||
[react/view components.styles/flex
|
|
||||||
[react/view {:flex-direction :row}
|
|
||||||
|
|
||||||
[react/view styles/gas-container-wrapper
|
|
||||||
[wallet.components/cartouche {}
|
|
||||||
(i18n/label :t/gas-limit)
|
|
||||||
[react/view styles/gas-input-wrapper
|
|
||||||
[react/text-input (merge styles/transaction-fee-input
|
|
||||||
{:on-change-text #(re-frame/dispatch [:wallet.send/edit-value :gas %])
|
|
||||||
:default-value gas
|
|
||||||
:accessibility-label :gas-limit-input})]]]
|
|
||||||
(when (:invalid? gas-edit)
|
|
||||||
[tooltip/tooltip (i18n/label :t/invalid-number)])]
|
|
||||||
|
|
||||||
[react/view styles/gas-container-wrapper
|
|
||||||
[wallet.components/cartouche {}
|
|
||||||
(i18n/label :t/gas-price)
|
|
||||||
[react/view styles/gas-input-wrapper
|
|
||||||
[react/text-input (merge styles/transaction-fee-input
|
|
||||||
{:on-change-text #(re-frame/dispatch [:wallet.send/edit-value :gas-price %])
|
|
||||||
:default-value gas-price
|
|
||||||
:accessibility-label :gas-price-input})]
|
|
||||||
[wallet.components/cartouche-secondary-text
|
|
||||||
(i18n/label :t/gwei)]]]
|
|
||||||
(when (:invalid? gas-price-edit)
|
|
||||||
[tooltip/tooltip (i18n/label (if (= :invalid-number (:invalid? gas-price-edit))
|
|
||||||
:t/invalid-number
|
|
||||||
:t/wallet-send-min-wei))])]]
|
|
||||||
|
|
||||||
[react/view styles/transaction-fee-info
|
|
||||||
[react/view styles/transaction-fee-info-icon
|
|
||||||
[react/text {:style styles/transaction-fee-info-icon-text} "?"]]
|
|
||||||
[react/view styles/transaction-fee-info-text-wrapper
|
|
||||||
[react/i18n-text {:style styles/advanced-fees-text
|
|
||||||
:key :wallet-transaction-fee-details}]]]
|
|
||||||
[components/separator]
|
|
||||||
[react/view styles/transaction-fee-block-wrapper
|
|
||||||
[wallet.components/cartouche {:disabled? true}
|
|
||||||
(i18n/label :t/amount)
|
|
||||||
[react/view {:accessibility-label :amount-input}
|
|
||||||
[wallet.components/cartouche-text-content
|
|
||||||
(str (money/to-fixed (money/internal->formatted amount symbol decimals)))
|
|
||||||
(name symbol)]]]
|
|
||||||
[wallet.components/cartouche {:disabled? true}
|
|
||||||
(i18n/label :t/wallet-transaction-total-fee)
|
|
||||||
[react/view {:accessibility-label :total-fee-input}
|
|
||||||
[wallet.components/cartouche-text-content
|
|
||||||
(str max-fee " " (i18n/label :t/eth))]]]]
|
|
||||||
|
|
||||||
[bottom-buttons/bottom-buttons styles/fee-buttons
|
|
||||||
[button/button {:on-press #(re-frame/dispatch [:wallet.send/reset-gas-default])
|
|
||||||
:accessibility-label :reset-to-default-button}
|
|
||||||
(i18n/label :t/reset-default)]
|
|
||||||
[button/button {:on-press #(do (re-frame/dispatch [:wallet.send/set-gas-details
|
|
||||||
(:value-number gas-edit)
|
|
||||||
(:value-number gas-price-edit)])
|
|
||||||
(return-to-transaction modal?))
|
|
||||||
:accessibility-label :done-button
|
|
||||||
:disabled? (or (:invalid? gas-edit)
|
|
||||||
(:invalid? gas-price-edit))}
|
|
||||||
(i18n/label :t/done)]]]])))
|
|
||||||
|
|
||||||
(defn- advanced-cartouche [{:keys [max-fee gas gas-price]}]
|
|
||||||
[react/view
|
|
||||||
[wallet.components/cartouche {:on-press #(do (re-frame/dispatch [:wallet.send/clear-gas])
|
|
||||||
(re-frame/dispatch [:navigate-to-modal :wallet-transaction-fee]))}
|
|
||||||
(i18n/label :t/wallet-transaction-fee)
|
|
||||||
[react/view {:style styles/advanced-options-text-wrapper
|
|
||||||
:accessibility-label :transaction-fee-button}
|
|
||||||
[react/text {:style styles/advanced-fees-text}
|
|
||||||
(str max-fee " " (i18n/label :t/eth))]
|
|
||||||
[react/text {:style styles/advanced-fees-details-text}
|
|
||||||
(str (money/to-fixed gas) " * " (money/to-fixed (money/wei-> :gwei gas-price)) (i18n/label :t/gwei))]]]])
|
|
||||||
|
|
||||||
(defn- advanced-options [advanced? transaction modal? scroll]
|
|
||||||
[react/view {:style styles/advanced-wrapper}
|
|
||||||
[react/touchable-highlight {:on-press (fn []
|
|
||||||
(re-frame/dispatch [:wallet.send/toggle-advanced (not advanced?)])
|
|
||||||
(when (and scroll @scroll) (utils/set-timeout #(.scrollToEnd @scroll) 350)))}
|
|
||||||
[react/view {:style styles/advanced-button-wrapper}
|
|
||||||
[react/view {:style styles/advanced-button
|
|
||||||
:accessibility-label :advanced-button}
|
|
||||||
[react/i18n-text {:style (merge wallet.components.styles/label
|
|
||||||
styles/advanced-label)
|
|
||||||
:key :wallet-advanced}]
|
|
||||||
[vector-icons/icon (if advanced? :icons/up :icons/down) {:color :white}]]]]
|
|
||||||
(when advanced?
|
|
||||||
[advanced-cartouche transaction])])
|
|
||||||
|
|
||||||
(defn- send-transaction-panel [{:keys [modal? transaction scroll advanced? network]}]
|
|
||||||
(let [{:keys [amount amount-text amount-error asset-error signing? to to-name sufficient-funds? sufficient-gas?
|
|
||||||
in-progress? from-chat? symbol]} transaction
|
|
||||||
{:keys [decimals] :as token} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)
|
|
||||||
timeout (atom nil)]
|
|
||||||
[wallet.components/simple-screen {:avoid-keyboard? (not modal?)
|
[wallet.components/simple-screen {:avoid-keyboard? (not modal?)
|
||||||
:status-bar-type (if modal? :modal-wallet :wallet)}
|
:status-bar-type (if modal? :modal-wallet :wallet)}
|
||||||
[toolbar from-chat? false (if modal? act/close-white act/back-white)
|
[toolbar modal? (i18n/label :t/send-transaction)]
|
||||||
(i18n/label :t/send-transaction)]
|
|
||||||
[react/view components.styles/flex
|
[react/view components.styles/flex
|
||||||
[common/network-info {:text-color :white}]
|
[common/network-info {:text-color :white}]
|
||||||
[react/scroll-view {:keyboard-should-persist-taps :always
|
[react/scroll-view {:keyboard-should-persist-taps :always
|
||||||
|
@ -247,46 +159,47 @@
|
||||||
:amount-text amount-text
|
:amount-text amount-text
|
||||||
:input-options {:on-focus (fn [] (when (and scroll @scroll) (utils/set-timeout #(.scrollToEnd @scroll) 100)))
|
:input-options {:on-focus (fn [] (when (and scroll @scroll) (utils/set-timeout #(.scrollToEnd @scroll) 100)))
|
||||||
:on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount % symbol decimals])}} token]
|
:on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount % symbol decimals])}} token]
|
||||||
[advanced-options advanced? transaction modal? scroll]]]
|
[advanced-options advanced? transaction scroll]]]
|
||||||
(if signing?
|
(if show-password-input?
|
||||||
[signing-buttons in-progress?
|
[enter-password-buttons in-progress?
|
||||||
#(re-frame/dispatch (if modal? [:wallet/cancel-signing-modal] [:wallet/discard-transaction]))
|
#(re-frame/dispatch [:wallet/cancel-entering-password])
|
||||||
#(re-frame/dispatch (if modal? [:wallet/sign-transaction-modal] [:wallet/sign-transaction]))
|
#(re-frame/dispatch [:wallet/send-transaction])
|
||||||
:t/transactions-sign-transaction]
|
:t/transactions-sign-transaction]
|
||||||
[sign-button amount-error to amount sufficient-funds? sufficient-gas? modal?])
|
[sign-transaction-button amount-error to amount sufficient-funds? sufficient-gas? modal?])
|
||||||
(when signing?
|
(when show-password-input?
|
||||||
[sign-panel :t/signing-phrase-description in-progress?])
|
[password-input-panel :t/signing-phrase-description in-progress?])
|
||||||
(when in-progress? [react/view styles/processing-view])]]))
|
(when in-progress? [react/view styles/processing-view])]]))
|
||||||
|
|
||||||
|
;; SEND TRANSACTION FROM WALLET (CHAT)
|
||||||
(defview send-transaction []
|
(defview send-transaction []
|
||||||
(letsubs [transaction [:wallet.send/transaction]
|
(letsubs [transaction [:wallet.send/transaction]
|
||||||
advanced? [:wallet.send/advanced?]
|
advanced? [:wallet.send/advanced?]
|
||||||
network [:get-current-account-network]
|
network [:get-current-account-network]
|
||||||
scroll (atom nil)]
|
scroll (atom nil)]
|
||||||
[send-transaction-panel {:modal? false :transaction transaction :scroll scroll :advanced? advanced?
|
[send-transaction-view {:modal? false :transaction transaction :scroll scroll :advanced? advanced?
|
||||||
:network network}]))
|
:network network}]))
|
||||||
|
|
||||||
|
;; SEND TRANSACTION FROM DAPP
|
||||||
(defview send-transaction-modal []
|
(defview send-transaction-modal []
|
||||||
(letsubs [transaction [:wallet.send/unsigned-transaction]
|
(letsubs [transaction [:wallet.send/transaction]
|
||||||
advanced? [:wallet.send/advanced?]
|
advanced? [:wallet.send/advanced?]
|
||||||
network [:get-current-account-network]
|
network [:get-current-account-network]
|
||||||
scroll (atom nil)]
|
scroll (atom nil)]
|
||||||
(if transaction
|
(if transaction
|
||||||
[send-transaction-panel {:modal? true :transaction transaction :scroll scroll :advanced? advanced?
|
[send-transaction-view {:modal? true :transaction transaction :scroll scroll :advanced? advanced?
|
||||||
:network network}]
|
:network network}]
|
||||||
[react/view wallet.styles/wallet-modal-container
|
[react/view wallet.styles/wallet-modal-container
|
||||||
[react/view components.styles/flex
|
[react/view components.styles/flex
|
||||||
[status-bar/status-bar {:type :modal-wallet}]
|
[status-bar/status-bar {:type :modal-wallet}]
|
||||||
[toolbar false false act/close-white
|
[toolbar true (i18n/label :t/send-transaction)]
|
||||||
(i18n/label :t/send-transaction)]
|
|
||||||
[react/i18n-text {:style styles/empty-text
|
[react/i18n-text {:style styles/empty-text
|
||||||
:key :unsigned-transaction-expired}]]])))
|
:key :unsigned-transaction-expired}]]])))
|
||||||
|
|
||||||
|
;; SIGN MESSAGE FROM DAPP
|
||||||
(defview sign-message-modal []
|
(defview sign-message-modal []
|
||||||
(letsubs [{:keys [data in-progress?]} [:wallet.send/unsigned-transaction]]
|
(letsubs [{:keys [data in-progress?]} [:wallet.send/transaction]]
|
||||||
[wallet.components/simple-screen {:status-bar-type :modal-wallet}
|
[wallet.components/simple-screen {:status-bar-type :modal-wallet}
|
||||||
[toolbar true false act/close-white
|
[toolbar true (i18n/label :t/sign-message)]
|
||||||
(i18n/label :t/sign-message)]
|
|
||||||
[react/view components.styles/flex
|
[react/view components.styles/flex
|
||||||
[react/scroll-view
|
[react/scroll-view
|
||||||
[react/view styles/send-transaction-form
|
[react/view styles/send-transaction-form
|
||||||
|
@ -297,10 +210,10 @@
|
||||||
:input-options {:multiline true}
|
:input-options {:multiline true}
|
||||||
:amount-text data}
|
:amount-text data}
|
||||||
nil]]]]
|
nil]]]]
|
||||||
[signing-buttons false
|
[enter-password-buttons false
|
||||||
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
|
#(re-frame/dispatch [:wallet/discard-transaction-navigate-back])
|
||||||
#(re-frame/dispatch [:wallet/sign-message-modal])
|
#(re-frame/dispatch [:wallet/sign-message])
|
||||||
:t/transactions-sign]
|
:t/transactions-sign]
|
||||||
[sign-panel :t/signing-message-phrase-description false]
|
[password-input-panel :t/signing-message-phrase-description false]
|
||||||
(when in-progress?
|
(when in-progress?
|
||||||
[react/view styles/processing-view])]]))
|
[react/view styles/processing-view])]]))
|
||||||
|
|
|
@ -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-macros [status-im.utils.styles :refer [defnstyle defstyle]])
|
||||||
(:require [status-im.ui.components.colors :as colors]))
|
(: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-macros [status-im.utils.views :refer [defview letsubs]])
|
||||||
(:require [status-im.ui.components.react :as react]
|
(:require [status-im.ui.components.react :as react]
|
||||||
[status-im.ui.components.status-bar.view :as status-bar]
|
[status-im.ui.components.status-bar.view :as status-bar]
|
||||||
[status-im.ui.components.icons.vector-icons :as vi]
|
[status-im.ui.components.icons.vector-icons :as vi]
|
||||||
[status-im.ui.screens.wallet.styles :as wallet.styles]
|
[status-im.ui.screens.wallet.styles :as wallet.styles]
|
||||||
[status-im.ui.screens.wallet.send.transaction-sent.styles :as styles]
|
[status-im.ui.screens.wallet.transaction-sent.styles :as styles]
|
||||||
[status-im.ui.components.styles :as components.styles]
|
[status-im.ui.components.styles :as components.styles]
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[status-im.ui.screens.wallet.components.views :as components]
|
[status-im.ui.screens.wallet.components.views :as components]
|
|
@ -42,45 +42,6 @@
|
||||||
(fn [transactions]
|
(fn [transactions]
|
||||||
(group-by :type (vals transactions))))
|
(group-by :type (vals transactions))))
|
||||||
|
|
||||||
(defn- format-unsigned-transaction [{:keys [id] :as transaction}]
|
|
||||||
(assoc transaction
|
|
||||||
:type :unsigned
|
|
||||||
:confirmations 0
|
|
||||||
;; TODO (andrey) revisit this, we shouldn't set not hash value to the hash field
|
|
||||||
:hash id))
|
|
||||||
|
|
||||||
(reg-sub :wallet/unsigned-transactions
|
|
||||||
:<- [:wallet]
|
|
||||||
:<- [:get-contacts-by-address]
|
|
||||||
(fn [[wallet contacts]]
|
|
||||||
(map #(enrich-transaction % contacts) (vals (:transactions-unsigned wallet)))))
|
|
||||||
|
|
||||||
(reg-sub :wallet.transactions/unsigned-transactions
|
|
||||||
:<- [:wallet/unsigned-transactions]
|
|
||||||
(fn [transactions]
|
|
||||||
(reduce (fn [acc {:keys [id] :as transaction}]
|
|
||||||
(assoc acc id (format-unsigned-transaction transaction)))
|
|
||||||
{}
|
|
||||||
transactions)))
|
|
||||||
|
|
||||||
(reg-sub :wallet.transactions/unsigned-transactions-count
|
|
||||||
:<- [:wallet.transactions/unsigned-transactions]
|
|
||||||
(fn [unsigned-transactions]
|
|
||||||
(count unsigned-transactions)))
|
|
||||||
|
|
||||||
(reg-sub :wallet.transactions/unsigned-transactions-list
|
|
||||||
:<- [:wallet.transactions/unsigned-transactions]
|
|
||||||
(fn [unsigned-transactions]
|
|
||||||
(vals unsigned-transactions)))
|
|
||||||
|
|
||||||
(reg-sub :wallet.transactions/postponed-transactions-list
|
|
||||||
:<- [:wallet.transactions/grouped-transactions]
|
|
||||||
(fn [{:keys [postponed]}]
|
|
||||||
(when postponed
|
|
||||||
{:title "Postponed"
|
|
||||||
:key :postponed
|
|
||||||
:data postponed})))
|
|
||||||
|
|
||||||
(reg-sub :wallet.transactions/pending-transactions-list
|
(reg-sub :wallet.transactions/pending-transactions-list
|
||||||
:<- [:wallet.transactions/grouped-transactions]
|
:<- [:wallet.transactions/grouped-transactions]
|
||||||
(fn [{:keys [pending]}]
|
(fn [{:keys [pending]}]
|
||||||
|
@ -113,12 +74,10 @@
|
||||||
(group-transactions-by-date (concat inbound outbound failed))))
|
(group-transactions-by-date (concat inbound outbound failed))))
|
||||||
|
|
||||||
(reg-sub :wallet.transactions/transactions-history-list
|
(reg-sub :wallet.transactions/transactions-history-list
|
||||||
:<- [:wallet.transactions/postponed-transactions-list]
|
|
||||||
:<- [:wallet.transactions/pending-transactions-list]
|
:<- [:wallet.transactions/pending-transactions-list]
|
||||||
:<- [:wallet.transactions/completed-transactions-list]
|
:<- [:wallet.transactions/completed-transactions-list]
|
||||||
(fn [[postponed pending completed]]
|
(fn [[pending completed]]
|
||||||
(cond-> []
|
(cond-> []
|
||||||
postponed (into postponed)
|
|
||||||
pending (into pending)
|
pending (into pending)
|
||||||
completed (into completed))))
|
completed (into completed))))
|
||||||
|
|
||||||
|
@ -128,13 +87,11 @@
|
||||||
(:current-transaction wallet)))
|
(:current-transaction wallet)))
|
||||||
|
|
||||||
(reg-sub :wallet.transactions/transaction-details
|
(reg-sub :wallet.transactions/transaction-details
|
||||||
:<- [:wallet.transactions/unsigned-transactions]
|
|
||||||
:<- [:wallet.transactions/transactions]
|
:<- [:wallet.transactions/transactions]
|
||||||
:<- [:wallet.transactions/current-transaction]
|
:<- [:wallet.transactions/current-transaction]
|
||||||
:<- [:network]
|
:<- [:network]
|
||||||
(fn [[unsigned-transactions transactions current-transaction network]]
|
(fn [[transactions current-transaction network]]
|
||||||
(let [transactions (merge transactions unsigned-transactions)
|
(let [{:keys [gas-used gas-price hash timestamp type] :as transaction} (get transactions current-transaction)
|
||||||
{:keys [gas-used gas-price hash timestamp type] :as transaction} (get transactions current-transaction)
|
|
||||||
chain (ethereum/network->chain-keyword network)]
|
chain (ethereum/network->chain-keyword network)]
|
||||||
(when transaction
|
(when transaction
|
||||||
(merge transaction
|
(merge transaction
|
||||||
|
|
|
@ -20,7 +20,9 @@
|
||||||
[status-im.utils.ethereum.tokens :as tokens]
|
[status-im.utils.ethereum.tokens :as tokens]
|
||||||
[status-im.constants :as constants]
|
[status-im.constants :as constants]
|
||||||
[status-im.utils.datetime :as datetime]
|
[status-im.utils.datetime :as datetime]
|
||||||
[clojure.string :as string])
|
[clojure.string :as string]
|
||||||
|
[status-im.utils.security :as security]
|
||||||
|
[status-im.utils.types :as types])
|
||||||
(:refer-clojure :exclude [name symbol]))
|
(:refer-clojure :exclude [name symbol]))
|
||||||
|
|
||||||
(defn name [web3 contract cb]
|
(defn name [web3 contract cb]
|
||||||
|
@ -42,12 +44,14 @@
|
||||||
(ethereum/call-params contract "balanceOf(address)" (ethereum/normalized-address address))
|
(ethereum/call-params contract "balanceOf(address)" (ethereum/normalized-address address))
|
||||||
#(cb %1 (ethereum/hex->bignumber %2))))
|
#(cb %1 (ethereum/hex->bignumber %2))))
|
||||||
|
|
||||||
(defn transfer [web3 contract from address value params cb]
|
(defn transfer [contract from to value gas gas-price masked-password on-completed]
|
||||||
(ethereum/send-transaction web3
|
(status/send-transaction (types/clj->json
|
||||||
(merge (ethereum/call-params contract "transfer(address,uint256)" (ethereum/normalized-address address) (ethereum/int->hex value))
|
(merge (ethereum/call-params contract "transfer(address,uint256)" to value)
|
||||||
{:from from}
|
{:from from
|
||||||
params)
|
:gas gas
|
||||||
#(cb %1 (ethereum/hex->boolean %2))))
|
:gasPrice gas-price}))
|
||||||
|
(security/unmask masked-password)
|
||||||
|
on-completed))
|
||||||
|
|
||||||
(defn transfer-from [web3 contract from-address to-address value cb]
|
(defn transfer-from [web3 contract from-address to-address value cb]
|
||||||
(ethereum/call web3
|
(ethereum/call web3
|
||||||
|
@ -168,7 +172,7 @@
|
||||||
(add-padding from)
|
(add-padding from)
|
||||||
(add-padding to)]}]}
|
(add-padding to)]}]}
|
||||||
payload (.stringify js/JSON (clj->js args))]
|
payload (.stringify js/JSON (clj->js args))]
|
||||||
(status/call-web3-private payload
|
(status/call-private-rpc payload
|
||||||
(response-handler web3 current-block-number chain direction ethereum/handle-error cb))))
|
(response-handler web3 current-block-number chain direction ethereum/handle-error cb))))
|
||||||
|
|
||||||
(defn get-token-transactions
|
(defn get-token-transactions
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
(defn make-internal-web3 []
|
(defn make-internal-web3 []
|
||||||
(dependencies/Web3.
|
(dependencies/Web3.
|
||||||
#js {:sendAsync (fn [payload callback]
|
#js {:sendAsync (fn [payload callback]
|
||||||
(status/call-web3-private
|
(status/call-private-rpc
|
||||||
(.stringify js/JSON payload)
|
(.stringify js/JSON payload)
|
||||||
(fn [response]
|
(fn [response]
|
||||||
(if (= "" response)
|
(if (= "" response)
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
status-im.ui.screens.db
|
status-im.ui.screens.db
|
||||||
status-im.ui.screens.subs
|
status-im.ui.screens.subs
|
||||||
[re-frame.core :as re-frame]
|
[re-frame.core :as re-frame]
|
||||||
[status-im.models.browser :as model]))
|
[status-im.models.browser :as model]
|
||||||
|
[status-im.utils.types :as types]))
|
||||||
|
|
||||||
(defn test-fixtures []
|
(defn test-fixtures []
|
||||||
|
|
||||||
|
@ -129,18 +130,18 @@
|
||||||
|
|
||||||
(is (zero? (count @dapps-permissions)))
|
(is (zero? (count @dapps-permissions)))
|
||||||
|
|
||||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||||
:host dapp-name
|
:host dapp-name
|
||||||
:permissions ["FAKE_PERMISSION"]}
|
:permissions ["FAKE_PERMISSION"]})
|
||||||
nil nil])
|
nil nil])
|
||||||
|
|
||||||
(is (= {:dapp dapp-name
|
(is (= {:dapp dapp-name
|
||||||
:permissions []}
|
:permissions []}
|
||||||
(get @dapps-permissions dapp-name)))
|
(get @dapps-permissions dapp-name)))
|
||||||
|
|
||||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||||
:host dapp-name
|
:host dapp-name
|
||||||
:permissions ["CONTACT_CODE"]}
|
:permissions ["CONTACT_CODE"]})
|
||||||
nil nil])
|
nil nil])
|
||||||
|
|
||||||
(is (= 1 (count @dapps-permissions)))
|
(is (= 1 (count @dapps-permissions)))
|
||||||
|
@ -149,9 +150,9 @@
|
||||||
:permissions ["CONTACT_CODE"]}
|
:permissions ["CONTACT_CODE"]}
|
||||||
(get @dapps-permissions dapp-name)))
|
(get @dapps-permissions dapp-name)))
|
||||||
|
|
||||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||||
:host dapp-name
|
:host dapp-name
|
||||||
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]}
|
:permissions ["CONTACT_CODE" "FAKE_PERMISSION"]})
|
||||||
nil nil])
|
nil nil])
|
||||||
|
|
||||||
(is (= 1 (count @dapps-permissions)))
|
(is (= 1 (count @dapps-permissions)))
|
||||||
|
@ -160,9 +161,9 @@
|
||||||
:permissions ["CONTACT_CODE"]}
|
:permissions ["CONTACT_CODE"]}
|
||||||
(get @dapps-permissions dapp-name)))
|
(get @dapps-permissions dapp-name)))
|
||||||
|
|
||||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||||
:host dapp-name
|
:host dapp-name
|
||||||
:permissions ["FAKE_PERMISSION"]}
|
:permissions ["FAKE_PERMISSION"]})
|
||||||
nil nil])
|
nil nil])
|
||||||
|
|
||||||
(is (= 1 (count @dapps-permissions)))
|
(is (= 1 (count @dapps-permissions)))
|
||||||
|
@ -171,9 +172,9 @@
|
||||||
:permissions ["CONTACT_CODE"]}
|
:permissions ["CONTACT_CODE"]}
|
||||||
(get @dapps-permissions dapp-name)))
|
(get @dapps-permissions dapp-name)))
|
||||||
|
|
||||||
(re-frame/dispatch [:on-bridge-message {:type "status-api-request"
|
(re-frame/dispatch [:on-bridge-message (types/clj->json {:type "status-api-request"
|
||||||
:host dapp-name2
|
:host dapp-name2
|
||||||
:permissions ["CONTACT_CODE"]}
|
:permissions ["CONTACT_CODE"]})
|
||||||
nil nil])
|
nil nil])
|
||||||
|
|
||||||
(is (= 2 (count @dapps-permissions)))
|
(is (= 2 (count @dapps-permissions)))
|
||||||
|
|
Loading…
Reference in New Issue