[#8666] Add account via BIP 44

Signed-off-by: Andrey Shovkoplyas <motor4ik@gmail.com>
This commit is contained in:
Andrey Shovkoplyas 2019-08-12 10:53:31 +02:00
parent fde55b80d0
commit ba112a765b
No known key found for this signature in database
GPG Key ID: EAAB7C8622D860A4
46 changed files with 757 additions and 331 deletions

View File

@ -306,6 +306,7 @@
:wallet-request-assets
:choose-recipient
:recent-recipients
:select-account
:wallet-send-transaction-request
:contact-code
:wallet-settings-hook)
@ -317,6 +318,7 @@
colors/white)})
bottom-background (when (#{:recent-recipients
:select-account
:wallet-send-assets
:wallet-request-assets} current-view)
[view {:background-color colors/white

View File

@ -244,6 +244,7 @@ var TopLevel = {
"messaging" : function () {},
"method" : function () {},
"minus" : function () {},
"plus" : function () {},
"mkdir" : function () {},
"module" : function () {},
"moveFile" : function () {},
@ -563,5 +564,9 @@ var TopLevel = {
"createAppContainer" : function () {},
"useScreens" : function () {},
"multiAccountGenerateAndDeriveAddresses" : function () {},
"multiAccountStoreDerived" : function () {}
"multiAccountStoreDerived" : function () {},
"multiAccountDeriveAddresses" : function () {},
"multiAccountReset" : function () {},
"multiAccountLoadAccount" : function () {},
"multiAccountStoreAccount" : function () {}
}

View File

@ -660,6 +660,82 @@ class StatusModule extends ReactContextBaseJavaModule implements LifecycleEventL
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountStoreAccount(final String json, final Callback callback) {
Log.d(TAG, "multiAccountStoreAccount");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountStoreAccount(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountLoadAccount(final String json, final Callback callback) {
Log.d(TAG, "multiAccountLoadAccount");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountLoadAccount(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountReset(final Callback callback) {
Log.d(TAG, "multiAccountReset");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountReset();
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountDeriveAddresses(final String json, final Callback callback) {
Log.d(TAG, "multiAccountDeriveAddresses");
if (!checkAvailability()) {
callback.invoke(false);
return;
}
Runnable r = new Runnable() {
@Override
public void run() {
String res = Statusgo.multiAccountDeriveAddresses(json);
callback.invoke(res);
}
};
StatusThreadPoolExecutor.getInstance().execute(r);
}
@ReactMethod
public void multiAccountGenerateAndDeriveAddresses(final String json, final Callback callback) {
Log.d(TAG, "multiAccountGenerateAndDeriveAddresses");

View File

@ -345,6 +345,35 @@ RCT_EXPORT_METHOD(multiAccountGenerateAndDeriveAddresses:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountStoreAccount
RCT_EXPORT_METHOD(multiAccountStoreAccount:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"MultiAccountStoreAccount() method called");
#endif
NSString *result = StatusgoMultiAccountStoreAccount(json);
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountLoadAccount
RCT_EXPORT_METHOD(multiAccountLoadAccount:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"MultiAccountLoadAccount() method called");
#endif
NSString *result = StatusgoMultiAccountLoadAccount(json);
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountReset
RCT_EXPORT_METHOD(multiAccountReset:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"MultiAccountReset() method called");
#endif
NSString *result = StatusgoMultiAccountReset();
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// multiAccountStoreDerived
RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
@ -355,6 +384,15 @@ RCT_EXPORT_METHOD(multiAccountStoreDerived:(NSString *)json
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// MultiAccountDeriveAddresses
RCT_EXPORT_METHOD(multiAccountDeriveAddresses:(NSString *)json
callback:(RCTResponseSenderBlock)callback) {
#if DEBUG
NSLog(@"MultiAccountDeriveAddresses() method called");
#endif
NSString *result = StatusgoMultiAccountDeriveAddresses(json);
callback(@[result]);
}
//////////////////////////////////////////////////////////////////// login
RCT_EXPORT_METHOD(login:(NSString *)json

View File

@ -60,8 +60,9 @@
(def assets-separator [react/view transactions-styles/asset-separator])
(defview choose-asset [nft?]
(letsubs [assets [:wallet/visible-assets-with-amount]]
(defn choose-asset [nft?] [react/view])
;;TODO we'll need to specify address here
#_(letsubs [assets [:wallet/visible-assets-with-amount]]
[react/view
[list/flat-list {:data (filter #(if nft?
(:nft? %)
@ -74,7 +75,7 @@
:enableEmptySections true
:separator assets-separator
:keyboardShouldPersistTaps :always
:bounces false}]]))
:bounces false}]])
(defn choose-asset-suggestion []
[choose-asset false])

View File

@ -237,6 +237,7 @@
(def ^:const status-create-address "status_createaddress")
(def ^:const path-root "m/44'/60'/0'/0")
(def ^:const path-default-wallet "m/44'/60'/0'/0/0")
(def ^:const path-whisper "m/43'/60'/1581'/0'/0")

View File

@ -255,3 +255,4 @@
(def v26 (update v25 :properties merge {:root-address {:type :string :optional true}
:accounts {:type :string :optional true}}))
(def v27 (update v26 :properties merge {:latest-derived-path {:type :int :optional true}}))

View File

@ -128,6 +128,11 @@
extension/v12
account/v26])
(def v32 [network/v1
bootnode/v4
extension/v12
account/v27])
;; put schemas ordered by version
(def schemas [{:schema v1
:schemaVersion 1
@ -221,4 +226,7 @@
:migration (constantly nil)}
{:schema v31
:schemaVersion 31
:migration (constantly nil)}
{:schema v32
:schemaVersion 32
:migration (constantly nil)}])

View File

@ -42,6 +42,7 @@
"status_startOneOnOneChat" {}
"status_removeChat" {}
"wallet_getTransfers" {}
"wallet_getTransfersByAddress" {}
"browsers_getBrowsers" {}
"browsers_addBrowser" {}
"browsers_deleteBrowser" {}

View File

@ -26,6 +26,7 @@
(fx/defn new-block
[{:keys [db] :as cofx} historical? block-number accounts]
(let [{:keys [:wallet/all-tokens]} db
accounts (get-in db [:multiaccount :accounts]) ;;TODO https://github.com/status-im/status-go/issues/1566
chain (ethereum/chain-keyword db)
chain-tokens (into {} (map (juxt :address identity)
(tokens/tokens-for all-tokens chain)))]
@ -34,20 +35,23 @@
(not historical?)
(assoc :db (assoc db :ethereum/current-block block-number))
(not-empty accounts)
(assoc ::transactions/get-transfers {:chain-tokens chain-tokens
true ;(not-empty accounts) ;;TODO https://github.com/status-im/status-go/issues/1566
(assoc ::transactions/get-transfers {:accounts accounts
:chain-tokens chain-tokens
:from-block block-number}))
(transactions/check-watched-transactions))))
(fx/defn reorg
[{:keys [db] :as cofx} block-number accounts]
(let [{:keys [:wallet/all-tokens]} db
accounts (get-in db [:multiaccount :accounts]) ;;TODO https://github.com/status-im/status-go/issues/1566
chain (ethereum/chain-keyword db)
chain-tokens (into {} (map (juxt :address identity)
(tokens/tokens-for all-tokens chain)))]
{:db (update-in db [:wallet :transactions]
wallet/remove-transactions-since-block block-number)
::transactions/get-transfers {:chain-tokens chain-tokens
::transactions/get-transfers {:accounts accounts
:chain-tokens chain-tokens
:from-block block-number}}))
(fx/defn new-wallet-event

View File

@ -119,36 +119,26 @@
watched-transactions))))
(fx/defn add-transfer
[{:keys [db] :as cofx} {:keys [hash id] :as transfer}]
(let [transfer-by-hash (get-in db [:wallet :transactions hash])
transfer-by-id (get-in db [:wallet :transaction id])
unique-id (when-not (or transfer-by-id
(= transfer transfer-by-hash))
(if (and transfer-by-hash
(not (= :pending
(:type transfer-by-hash))))
id
hash))]
(when unique-id
[{:keys [db] :as cofx} {:keys [hash id] :as transfer} address]
(let [transfer-by-hash (get-in db [:wallet :accounts address :transactions hash])]
;;transfer-by-id (get-in db [:wallet :transaction id]) ;; TODO didn't found any usage of this
(when-let [unique-id (when (not= transfer transfer-by-hash) ;(or transfer-by-id)
(if (and transfer-by-hash
(not (= :pending
(:type transfer-by-hash))))
id
hash))]
(fx/merge cofx
{:db (assoc-in db [:wallet :transactions unique-id]
{:db (assoc-in db [:wallet :accounts address :transactions unique-id]
(assoc transfer :hash unique-id))}
(check-transaction transfer)))))
(fx/defn new-transfers
{:events [::new-transfers]}
[{:keys [db] :as cofx} transfers]
(let [add-transfers-fx (map add-transfer transfers)]
[{:keys [db] :as cofx} address transfers]
(let [add-transfers-fx (map #(add-transfer % address) transfers)]
(apply fx/merge cofx (conj add-transfers-fx
wallet/update-balances))))
(fx/defn handle-history
[{:keys [db] :as cofx} transactions]
(fx/merge cofx
{:db (update-in db
[:wallet :transactions]
#(merge transactions %))}
wallet/update-balances))
(wallet/update-balances [address])))))
(fx/defn handle-token-history
[{:keys [db]} transactions]
@ -158,21 +148,22 @@
(re-frame/reg-fx
::get-transfers
(fn [{:keys [chain-tokens from-block to-block]
(fn [{:keys [accounts chain-tokens from-block to-block]
:or {from-block "0"
to-block nil}}]
;; start inbound token transaction subscriptions
;; outbound token transactions are already caught in new blocks filter
(json-rpc/call
{:method "wallet_getTransfers"
:params [(encode/uint from-block) (encode/uint to-block)]
:on-success #(re-frame/dispatch
[::new-transfers (enrich-transfers chain-tokens %)])})))
(doseq [{:keys [address]} accounts]
(json-rpc/call
{:method "wallet_getTransfersByAddress"
:params [address (encode/uint from-block) (encode/uint to-block)]
:on-success #(re-frame/dispatch [::new-transfers address (enrich-transfers chain-tokens %)])}))))
(fx/defn initialize
[{:keys [db] :as cofx}]
(let [{:keys [:wallet/all-tokens]} db
(let [accounts (get-in db [:multiaccount :accounts]) ;;TODO https://github.com/status-im/status-go/issues/1566
{:keys [:wallet/all-tokens]} db
chain (ethereum/chain-keyword db)
chain-tokens (into {} (map (juxt :address identity)
(tokens/tokens-for all-tokens chain)))]
{::get-transfers {:chain-tokens chain-tokens}}))
{::get-transfers {:accounts accounts :chain-tokens chain-tokens}}))

View File

@ -1788,12 +1788,6 @@
(fn [cofx [_ id handler]]
(ethereum.subscriptions/register-subscription cofx id handler)))
;; ethereum transactions events
(handlers/register-handler-fx
:ethereum.transactions.callback/fetch-history-success
(fn [cofx [_ transactions]]
(ethereum.transactions/handle-history cofx transactions)))
(handlers/register-handler-fx
:ethereum.transactions.callback/etherscan-error
(fn [cofx [event error]]
@ -1834,8 +1828,8 @@
(handlers/register-handler-fx
:wallet/token-found
(fn [cofx [_ symbol balance]]
(wallet/configure-token-balance-and-visibility cofx symbol balance)))
(fn [cofx [_ address symbol balance]]
(wallet/configure-token-balance-and-visibility cofx address symbol balance)))
(handlers/register-handler-fx
:wallet.settings.ui/navigate-back-pressed
@ -1844,12 +1838,12 @@
(when on-close
{:dispatch on-close})
(navigation/navigate-back)
(wallet/update-balances))))
(wallet/update-balances nil))))
(handlers/register-handler-fx
:wallet.callback/update-balance-success
(fn [cofx [_ balance]]
(wallet/update-balance cofx balance)))
(fn [cofx [_ address balance]]
(wallet/update-balance cofx address balance)))
(handlers/register-handler-fx
:wallet.callback/update-balance-fail
@ -1858,8 +1852,8 @@
(handlers/register-handler-fx
:wallet.callback/update-token-balance-success
(fn [cofx [_ symbol balance]]
(wallet/update-token-balance cofx symbol balance)))
(fn [cofx [_ address symbol balance]]
(wallet/update-token-balance cofx address symbol balance)))
(handlers/register-handler-fx
:wallet.callback/update-token-balance-fail
@ -1878,8 +1872,8 @@
(handlers/register-handler-fx
:wallet.ui/show-transaction-details
(fn [cofx [_ hash]]
(wallet/open-transaction-details cofx hash)))
(fn [cofx [_ hash address]]
(wallet/open-transaction-details cofx hash address)))
(handlers/register-handler-fx
:wallet.setup.ui/navigate-back-pressed

View File

@ -48,11 +48,13 @@
(defn create-multiaccount! [{:keys [id password]}]
(if id
(status/multiaccount-store-derived
id
[constants/path-whisper constants/path-default-wallet]
password
#(re-frame/dispatch [:multiaccounts.create.callback/create-multiaccount-success password]))
(do
;(status/multiaccount-store-account id password #()) ;; TODO if i add this, i'm unable to login after
(status/multiaccount-store-derived
id
[constants/path-whisper constants/path-default-wallet]
password
#(re-frame/dispatch [:multiaccounts.create.callback/create-multiaccount-success password])))
(status/create-multiaccount
password
#(re-frame/dispatch [:multiaccounts.create.callback/create-multiaccount-success (types/json->clj %) password]))))
@ -243,6 +245,7 @@
new-multiaccount {;;multiaccount
:root-address (:address multiaccount)
:public-key publicKey
:latest-derived-path 0
:installation-id (get-in db [:multiaccounts/new-installation-id]) ;;TODO why can't we generate it here?
:address address
:name (gfycat/generate-gfy publicKey)

View File

@ -90,7 +90,7 @@
(fx/defn initialize-wallet [cofx]
(fx/merge cofx
(wallet/initialize-tokens)
(wallet/update-balances)
(wallet/update-balances nil)
(wallet/update-prices)
(transactions/initialize)))

View File

@ -19,6 +19,18 @@
(defn create-multiaccount [password callback]
(native-module/create-account password callback))
(defn multiaccount-store-account [account-id password callback]
(native-module/multiaccount-store-account account-id password callback))
(defn multiaccount-load-account [address password callback]
(native-module/multiaccount-load-account address password callback))
(defn multiaccount-reset [callback]
(native-module/multiaccount-reset callback))
(defn multiaccount-derive-addresses [account-id paths callback]
(native-module/multiaccount-derive-addresses account-id paths callback))
(defn recover-multiaccount [passphrase password callback]
(native-module/recover-account passphrase password callback))

View File

@ -71,6 +71,32 @@
:paths paths})
on-result)))
(defn multiaccount-derive-addresses [account-id paths on-result]
(when (and @node-started (status))
(.multiAccountDeriveAddresses (status)
(types/clj->json {:accountID account-id
:paths paths})
on-result)))
(defn multiaccount-store-account [account-id password on-result]
(when (and @node-started (status))
(.multiAccountStoreAccount (status)
(types/clj->json {:accountID account-id
:password password})
on-result)))
(defn multiaccount-load-account [address password on-result]
(when (and @node-started (status))
(.multiAccountLoadAccount (status)
(types/clj->json {:address address
:password password})
on-result)))
(defn multiaccount-reset [on-result]
(when (and @node-started (status))
(.multiAccountReset (status)
on-result)))
(defn multiaccount-store-derived [account-id paths password on-result]
(when (and @node-started (status))
(.multiAccountStoreDerived (status)

View File

@ -137,24 +137,25 @@
:token token
:symbol symbol}))))))
(defn parse-tx-obj [db {:keys [to value data]}]
(if (nil? to)
{:contact {:name (i18n/label :t/new-contract)}}
(let [eth-value (when value (money/bignumber value))
eth-amount (when eth-value (money/to-number (money/wei->ether eth-value)))
token (get-transfer-token db to data)]
(cond
(and eth-amount (or (not (zero? eth-amount)) (nil? data)))
{:to to
:contact (get-contact db to)
:symbol :ETH
:amount (str eth-amount)
:token (tokens/asset-for (:wallet/all-tokens db) (ethereum/chain-keyword db) :ETH)}
(not (nil? token))
token
:else
{:to to
:contact {:address (ethereum/normalized-address to)}}))))
(defn parse-tx-obj [db {:keys [from to value data]}]
(merge {:from {:address from}}
(if (nil? to)
{:contact {:name (i18n/label :t/new-contract)}}
(let [eth-value (when value (money/bignumber value))
eth-amount (when eth-value (money/to-number (money/wei->ether eth-value)))
token (get-transfer-token db to data)]
(cond
(and eth-amount (or (not (zero? eth-amount)) (nil? data)))
{:to to
:contact (get-contact db to)
:symbol :ETH
:amount (str eth-amount)
:token (tokens/asset-for (:wallet/all-tokens db) (ethereum/chain-keyword db) :ETH)}
(not (nil? token))
token
:else
{:to to
:contact {:address (ethereum/normalized-address to)}})))))
(defn prepare-tx [db {{:keys [data gas gasPrice] :as tx-obj} :tx-obj :as tx}]
(merge

View File

@ -181,6 +181,7 @@
(reg-root-key-sub :intro-wizard :intro-wizard)
(reg-root-key-sub :popover/popover :popover/popover)
(reg-root-key-sub :generate-account :generate-account)
;;GENERAL ==============================================================================================================
@ -800,6 +801,7 @@
(re-frame/reg-sub
:chats/transaction-status
;;TODO address here for transactions
:<- [:wallet/transactions]
:<- [:ethereum/current-block]
(fn [[transactions current-block] [_ hash]]
@ -965,8 +967,21 @@
(re-frame/reg-sub
:balance
:<- [:wallet]
(fn [wallet [_ address]]
(get-in wallet [:accounts address :balance])))
(re-frame/reg-sub
:balance-default
:<- [:wallet]
:<- [:multiaccount]
(fn [[wallet {:keys [accounts]}]]
(get-in wallet [:accounts (:address (ethereum/get-default-account accounts)) :balance])))
(re-frame/reg-sub
:balances
:<- [:wallet]
(fn [wallet]
(:balance wallet)))
(map :balance (vals (:accounts wallet)))))
(re-frame/reg-sub
:price
@ -986,20 +1001,6 @@
(fn [settings]
(or (get-in settings [:wallet :currency]) :usd)))
(re-frame/reg-sub
:asset-value
(fn [[_ fsym decimals tsym]]
[(re-frame/subscribe [:balance])
(re-frame/subscribe [:price fsym tsym])
(re-frame/subscribe [:wallet/currency])])
(fn [[balance price currency] [_ fsym decimals tsym]]
(when (and balance price)
(-> (money/internal->formatted (get balance fsym) fsym decimals)
(money/crypto->fiat price)
(money/with-precision 2)
str
(i18n/format-currency (:code currency))))))
(defn- get-balance-total-value
[balance prices currency token->decimals]
(reduce-kv (fn [acc symbol value]
@ -1012,21 +1013,39 @@
(re-frame/reg-sub
:portfolio-value
:<- [:balance]
:<- [:balances]
:<- [:prices]
:<- [:wallet/currency]
:<- [:ethereum/chain-keyword]
:<- [:wallet/all-tokens]
(fn [[balance prices currency chain all-tokens] [_ currency-code]]
(fn [[balances prices currency chain all-tokens]]
(if (and balances prices)
(let [assets (tokens/tokens-for all-tokens chain)
token->decimals (into {} (map #(vector (:symbol %) (:decimals %)) assets))
currency-key (-> currency :code keyword)
balance-total-value (apply + (map #(get-balance-total-value % prices currency-key token->decimals) balances))]
(if (pos? balance-total-value)
(-> balance-total-value
(money/with-precision 2)
str
(i18n/format-currency (:code currency) false))
"0"))
"...")))
(re-frame/reg-sub
:account-portfolio-value
(fn [[_ address] _]
[(re-frame/subscribe [:balance address])
(re-frame/subscribe [:prices])
(re-frame/subscribe [:wallet/currency])
(re-frame/subscribe [:ethereum/chain-keyword])
(re-frame/subscribe [:wallet/all-tokens])])
(fn [[balance prices currency chain all-tokens]]
(if (and balance prices)
(let [assets (tokens/tokens-for all-tokens chain)
token->decimals (into {} (map #(vector (:symbol %) (:decimals %)) assets))
balance-total-value
(get-balance-total-value balance
prices
(or currency-code
(-> currency :code keyword))
token->decimals)]
(let [assets (tokens/tokens-for all-tokens chain)
token->decimals (into {} (map #(vector (:symbol %) (:decimals %)) assets))
currency-key (-> currency :code keyword)
balance-total-value (get-balance-total-value balance prices currency-key token->decimals)]
(if (pos? balance-total-value)
(-> balance-total-value
(money/with-precision 2)
@ -1050,12 +1069,6 @@
(let [vt-set (set visible-tokens)]
(group-by :custom? (map #(assoc % :checked? (boolean (get vt-set (keyword (:symbol %))))) all-tokens)))))
(re-frame/reg-sub
:wallet/balance-loading?
:<- [:wallet]
(fn [wallet]
(:balance-loading? wallet)))
(re-frame/reg-sub
:wallet/error-message
:<- [:wallet]
@ -1082,18 +1095,19 @@
(re-frame/reg-sub
:wallet/visible-assets-with-amount
:<- [:balance]
:<- [:wallet/visible-assets]
(fn [[_ address] _]
[(re-frame/subscribe [:balance address])
(re-frame/subscribe [:wallet/visible-assets])])
(fn [[balance visible-assets]]
(map #(assoc % :amount (get balance (:symbol %))) visible-assets)))
(defn update-value [balance prices currency]
(fn [{:keys [symbol decimals] :as token}]
(defn update-value [prices currency]
(fn [{:keys [symbol decimals amount] :as token}]
(let [price (get-in prices [symbol (-> currency :code keyword) :price])]
(assoc token
:price price
:value (when (and balance price)
(-> (money/internal->formatted (get balance symbol) symbol decimals)
:value (when (and amount price)
(-> (money/internal->formatted amount symbol decimals)
(money/crypto->fiat price)
(money/with-precision 2)
str
@ -1101,19 +1115,45 @@
(re-frame/reg-sub
:wallet/visible-assets-with-values
:<- [:wallet/visible-assets-with-amount]
(fn [[_ address] _]
[(re-frame/subscribe [:wallet/visible-assets-with-amount address])
(re-frame/subscribe [:prices])
(re-frame/subscribe [:wallet/currency])])
(fn [[assets prices currency]]
(let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets)
tokens-with-values (map (update-value prices currency) tokens)]
{:tokens tokens-with-values
:nfts nfts})))
(defn get-asset-amount [balances sym]
(reduce #(if-let [bl (get %2 sym)]
(.plus %1 bl)
%1)
(money/bignumber 0)
balances))
(re-frame/reg-sub
:wallet/all-visible-assets-with-amount
:<- [:balances]
:<- [:wallet/visible-assets]
(fn [[balances visible-assets]]
(map #(assoc % :amount (get-asset-amount balances (:symbol %))) visible-assets)))
(re-frame/reg-sub
:wallet/all-visible-assets-with-values
:<- [:wallet/all-visible-assets-with-amount]
:<- [:prices]
:<- [:wallet/currency]
:<- [:balance]
(fn [[assets prices currency balance]]
(fn [[assets prices currency]]
(let [{:keys [tokens nfts]} (group-by #(if (:nft? %) :nfts :tokens) assets)
tokens-with-values (map (update-value balance prices currency) tokens)]
tokens-with-values (map (update-value prices currency) tokens)]
{:tokens tokens-with-values
:nfts nfts})))
(re-frame/reg-sub
:wallet/transferrable-assets-with-amount
:<- [:wallet/visible-assets-with-amount]
(fn [[_ address]]
(re-frame/subscribe [:wallet/visible-assets-with-amount address]))
(fn [all-assets]
(filter #(not (:nft? %)) all-assets)))
@ -1128,8 +1168,8 @@
(re-frame/reg-sub
:wallet/transactions
:<- [:wallet]
(fn [wallet]
(get wallet :transactions)))
(fn [wallet [_ address]]
(get-in wallet [:accounts address :transactions])))
(re-frame/reg-sub
:wallet/filters
@ -1161,14 +1201,15 @@
(re-frame/reg-sub
:wallet.transactions/transactions
:<- [:wallet/transactions]
:<- [:contacts/contacts-by-address]
:<- [:ethereum/native-currency]
(fn [[_ address] _]
[(re-frame/subscribe [:wallet/transactions address])
(re-frame/subscribe [:contacts/contacts-by-address])
(re-frame/subscribe [:ethereum/native-currency])])
(fn [[transactions contacts native-currency]]
(reduce (fn [acc [hash transaction]]
(assoc acc
hash
(enrich-transaction transaction contacts native-currency)))
(enrich-transaction transaction contacts native-currency))) ;;TODO this doesn't look good for performance, we need to calculate this only once for each transaction
{}
transactions)))
@ -1212,7 +1253,8 @@
(defn- enrich-transaction-for-list
[filters
{:keys [type from-contact from to-contact to hash timestamp] :as transaction}]
{:keys [type from-contact from to-contact to hash timestamp] :as transaction}
address]
(when (filters type)
(assoc (case type
:inbound
@ -1229,7 +1271,7 @@
:contact to-contact
:address to))
:time-formatted (datetime/timestamp->time timestamp)
:on-touch-fn #(re-frame/dispatch [:wallet.ui/show-transaction-details hash]))))
:on-touch-fn #(re-frame/dispatch [:wallet.ui/show-transaction-details hash address]))))
(defn- group-transactions-by-date
[transactions]
@ -1243,33 +1285,28 @@
(re-frame/reg-sub
:wallet.transactions.history/screen
:<- [:wallet.transactions/transactions]
:<- [:wallet/filters]
:<- [:wallet.transactions/all-filters?]
(fn [[transactions filters all-filters?]]
(fn [[_ address] _]
[(re-frame/subscribe [:wallet.transactions/transactions address])
(re-frame/subscribe [:wallet/filters])
(re-frame/subscribe [:wallet.transactions/all-filters?])])
(fn [[transactions filters all-filters?] [_ address]]
{:all-filters? all-filters?
:transaction-history-sections
(->> transactions
vals
(keep #(enrich-transaction-for-list filters %))
(keep #(enrich-transaction-for-list filters % address))
(group-transactions-by-date))}))
(re-frame/reg-sub
:wallet.transactions/current-transaction
:<- [:wallet]
(fn [wallet]
(:current-transaction wallet)))
(re-frame/reg-sub
:wallet.transactions.details/current-transaction
:<- [:wallet.transactions/transactions]
:<- [:wallet.transactions/current-transaction]
:<- [:ethereum/native-currency]
:<- [:ethereum/chain-keyword]
(fn [[transactions current-transaction native-currency chain-keyword]]
(fn [[_ hash address] _]
[(re-frame/subscribe [:wallet.transactions/transactions address])
(re-frame/subscribe [:ethereum/native-currency])
(re-frame/subscribe [:ethereum/chain-keyword])])
(fn [[transactions native-currency chain-keyword] [_ hash _]]
(let [{:keys [gas-used gas-price hash timestamp type token value]
:as transaction}
(get transactions current-transaction)
(get transactions hash)
native-currency-text (name (or (:symbol-display native-currency)
(:symbol native-currency)))]
(when transaction
@ -1301,8 +1338,9 @@
(re-frame/reg-sub
:wallet.transactions.details/screen
:<- [:wallet.transactions.details/current-transaction]
:<- [:ethereum/current-block]
(fn [[_ hash address] _]
[(re-frame/subscribe [:wallet.transactions.details/current-transaction hash address])
(re-frame/subscribe [:ethereum/current-block])])
(fn [[transaction current-block]]
(let [confirmations (wallet.db/get-confirmations transaction
current-block)]
@ -1358,11 +1396,12 @@
(re-frame/reg-sub
:wallet.send/transaction
:<- [::send-transaction]
:<- [:balance]
(fn [[{:keys [amount symbol] :as transaction} balance]]
(-> transaction
(check-sufficient-funds balance symbol amount)
(check-sufficient-gas balance symbol amount))))
:<- [:wallet]
(fn [[{:keys [amount symbol from] :as transaction} wallet]]
(let [balance (get-in wallet [:accounts from :balance])]
(-> transaction
(check-sufficient-funds balance symbol amount)
(check-sufficient-gas balance symbol amount)))))
(re-frame/reg-sub
:wallet/settings
@ -1960,8 +1999,9 @@
(re-frame/reg-sub
:signing/amount-errors
:<- [:signing/tx]
:<- [:balance]
(fn [[_ address] _]
[(re-frame/subscribe [:signing/tx])
(re-frame/subscribe [:balance address])])
(fn [[{:keys [amount token gas gasPrice approve?]} balance]]
(if (and amount token (not approve?))
(let [amount-bn (money/formatted->internal (money/bignumber amount) (:symbol token) (:decimals token))

View File

@ -57,3 +57,11 @@
(def text-gray gray)
(def default-chat-color "#a187d5") ;; legacy
(def account-colors ["#9B832F"
"#D37EF4"
"#1D806F"
"#FA6565"
"#7CDA00"
"#887AF9"
"#8B3131"])

View File

@ -4,15 +4,16 @@
[status-im.ui.components.react :as react]
[status-im.ui.components.list-item.styles :as styles]
[status-im.utils.image :as utils.image]
[status-im.ui.components.tooltip.views :as tooltip]))
[status-im.ui.components.tooltip.views :as tooltip]
[status-im.ui.components.action-button.styles :as st]))
; type - optional :default , :small
; accessories - optional vector of :chevron, :check or component or string
; theme - optional :default, :wallet
; theme - optional :default, :wallet, :action
(defn list-item [{:keys [title subtitle accessories image image-path type theme on-press error content] :or {type :default theme :default}}]
(defn list-item [{:keys [title subtitle accessories image image-path icon type theme on-press error content] :or {type :default theme :default}}]
(let [small? (= :small type)]
[react/touchable-highlight {:on-press on-press :disabled (not on-press)}
[react/view {:style (styles/container small?)}
@ -22,6 +23,13 @@
(if (vector? image)
image
[image])])
;;Icon
(when icon
[react/view {:margin-left 16}
[react/view (when (= theme :action) (st/action-button-icon-container nil))
[icons/icon icon (if (= theme :action)
st/action-button-label
{:color colors/gray-transparent-40})]]])
(when image-path
[react/view {:margin-left 16}
[react/image {:source (utils.image/source image-path)
@ -29,7 +37,8 @@
;;Title
(when title
[react/view {:style {:margin-left 16 :margin-right 16}}
[react/text {:style (styles/title small? subtitle)
[react/text {:style (merge (styles/title small? subtitle)
(when (= theme :action) st/action-button-label))
:number-of-lines 1
:ellipsize-mode :tail}
title]

View File

@ -60,6 +60,7 @@
:wallet-send-assets {:type :wallet}
:wallet-request-assets {:type :wallet}
:recent-recipients {:type :wallet}
:select-account {:type :wallet}
:contact-code {:type :wallet}
:wallet-send-transaction-request {:type :wallet}
:wallet-settings-assets {:type :main}

View File

@ -358,4 +358,5 @@
::collectibles
::extensions-store
:registry/registry
::two-pane-ui-enabled?]))
::two-pane-ui-enabled?
::generate-account]))

View File

@ -55,7 +55,7 @@
(hide-panel-anim bottom-anim-value alpha-value (- window-height)))))
:reagent-render (fn []
(when @current-popover
(let [view (:view @current-popover)]
(let [{:keys [view style]} @current-popover]
[react/view {:position :absolute :top 0 :bottom 0 :left 0 :right 0}
[react/animated-view {:style {:flex 1 :background-color :black :opacity alpha-value}}]
[react/animated-view {:style
@ -66,13 +66,14 @@
:transform [{:translateY bottom-anim-value}]}}
[react/touchable-highlight {:style {:flex 1} :on-press #(re-frame/dispatch [:hide-popover])}
[react/view {:flex 1 :align-items :center :justify-content :center}
[react/view {:background-color :white
:border-radius 16
:margin 32
:shadow-offset {:width 0 :height 2}
:shadow-radius 8
:shadow-opacity 1
:shadow-color "rgba(0, 9, 26, 0.12)"}
[react/view (merge {:background-color :white
:border-radius 16
:margin 32
:shadow-offset {:width 0 :height 2}
:shadow-radius 8
:shadow-opacity 1
:shadow-color "rgba(0, 9, 26, 0.12)"}
style)
(cond
(vector? view)
view

View File

@ -64,7 +64,8 @@
[status-im.ui.screens.wallet.transactions.views :as wallet-transactions]
[status-im.ui.screens.wallet.custom-tokens.views :as custom-tokens]
[status-im.ui.screens.wallet.accounts.views :as wallet.accounts]
[status-im.ui.screens.wallet.account.views :as wallet.account]))
[status-im.ui.screens.wallet.account.views :as wallet.account]
[status-im.ui.screens.wallet.add-new.views :as add-account]))
(def all-screens
{:login login/login
@ -140,12 +141,11 @@
:contact-code wallet.components/contact-code
:wallet-send-transaction send/send-transaction
:recent-recipients wallet.components/recent-recipients
:select-account wallet.components/accounts
:recipient-qr-code wallet.components/recipient-qr-code
:wallet-send-assets wallet.components/send-assets
:wallet-send-transaction-request request/send-transaction-request
:wallet-request-assets wallet.components/request-assets
:unsigned-transactions wallet-transactions/transactions
:transactions-history wallet-transactions/transactions
:wallet-transaction-details wallet-transactions/transaction-details
:wallet-settings-hook wallet-settings/settings-hook
:selection-modal-screen [:modal extensions.module/selection-modal-screen-view]
@ -186,7 +186,10 @@
:keycard-settings hardwallet.settings/keycard-settings
:mobile-network-settings mobile-network-settings/mobile-network-settings
:welcome [:modal home/welcome]
:keycard-welcome keycard/welcome})
:keycard-welcome keycard/welcome
:add-new-account add-account/add-account
:add-new-account-password add-account/password
:account-added add-account/account-added})
(defn get-screen [screen]
(get all-screens screen #(throw (str "Screen " screen " is not defined."))))

View File

@ -4,12 +4,16 @@
{:name :wallet-stack
:screens [:wallet
:wallet-account
:add-new-account
:add-new-account-password
:account-added
:collectibles-list
:wallet-onboarding-setup
:contact-code
{:name :send-transaction-stack
:screens [:wallet-send-transaction
:recent-recipients
:select-account
:enter-pin-sign
:hardwallet-connect-sign
:recipient-qr-code
@ -18,8 +22,6 @@
:screens [:wallet-send-transaction-request
:wallet-request-assets
:recent-recipients]}
:unsigned-transactions
:transactions-history
:wallet-transaction-details
:wallet-settings-hook
:extension-screen-holder

View File

@ -55,9 +55,9 @@
(multiaccounts/displayed-name contact)
(:address contact)))
(defn contact-item [contact]
(defn contact-item [title contact]
[list-item/list-item {:type :small
:title (i18n/label :t/to)
:title title
:accessories [[react/text {:ellipsize-mode :middle :number-of-lines 1 :style {:flex-wrap :wrap}}
(displayed-name contact)]]}])
@ -205,11 +205,11 @@
[react/text (or formatted-data "")]]]
[password-view sign]]]))
(views/defview sheet [{:keys [contact amount token approve?] :as tx}]
(views/defview sheet [{:keys [from contact amount token approve?] :as tx}]
(views/letsubs [fee [:signing/fee]
sign [:signing/sign]
chain [:ethereum/chain-keyword]
{:keys [amount-error gas-error]} [:signing/amount-errors]
{:keys [amount-error gas-error]} [:signing/amount-errors (:address from)]
keycard-multiaccount? [:keycard-multiaccount?]]
(let [display-symbol (wallet.utils/display-symbol token)
fee-display-symbol (wallet.utils/display-symbol (tokens/native-currency chain))]
@ -220,7 +220,9 @@
[react/view {:padding-top 20}
[password-view sign]]
[react/view
[contact-item contact]
[contact-item (i18n/label :t/from) from]
[separator]
[contact-item (i18n/label :t/to) contact]
[separator]
[token-item token display-symbol]
(when-not approve?
@ -286,4 +288,4 @@
(views/letsubs [tx [:signing/tx]
{window-height :height} [:dimensions/window]]
;;we use select-keys here because we don't want to update view if other keys in map is changed
[signing-view (when tx (select-keys tx [:contact :amount :token :approve? :message])) window-height]))
[signing-view (when tx (select-keys tx [:from :contact :amount :token :approve? :message])) window-height]))

View File

@ -23,7 +23,7 @@
(defview price-badge [price id owned pending]
(letsubs [chain [:ethereum/chain-keyword]
balance [:balance]]
balance [:balance-default]]
(let [snt (money/to-number (if (= :mainnet chain) (:SNT balance) (:STT balance)))
not-enough-snt? (> price snt)
no-snt? (or (nil? snt) (zero? snt))]

View File

@ -1,9 +1,10 @@
(ns status-im.ui.screens.wallet.account.styles
(:require [status-im.ui.components.colors :as colors]))
(defn card [window-width]
{:width (- window-width 64) :height 161
:background-color colors/blue
(defn card [window-width color]
{:width (- window-width 30)
:height 161
:background-color color
:shadow-offset {:width 0 :height 2}
:shadow-radius 8
:shadow-opacity 1

View File

@ -38,11 +38,11 @@
[icons/icon icon {:color colors/white}]
[react/text {:style {:margin-left 8 :color colors/white}} label]]]])
(views/defview account-card [{:keys [address]}]
(views/defview account-card [{:keys [address color]}]
(views/letsubs [currency [:wallet/currency]
portfolio-value [:portfolio-value]
portfolio-value [:account-portfolio-value address]
window-width [:dimensions/window-width]]
[react/view {:style (styles/card window-width)}
[react/view {:style (styles/card window-width color)}
[react/view {:padding 16 :padding-bottom 12 :flex 1 :justify-content :space-between}
[react/nested-text {:style {:color colors/white-transparent :line-height 38
:font-weight "600" :font-size 32}}
@ -66,20 +66,19 @@
[react/view {:style styles/divider}]
[button (i18n/label :t/receive) :main-icons/receive #(re-frame/dispatch [:show-popover {:view :share-account :address address}])]]]))
(views/defview transactions []
(views/defview transactions [address]
(views/letsubs [{:keys [transaction-history-sections]}
[:wallet.transactions.history/screen]]
[:wallet.transactions.history/screen address]]
[history/history-list transaction-history-sections]))
(views/defview assets-and-collections []
(views/letsubs [{:keys [tokens nfts]} [:wallet/visible-assets-with-values]
(views/defview assets-and-collections [address]
(views/letsubs [{:keys [tokens nfts]} [:wallet/visible-assets-with-values address]
currency [:wallet/currency]]
(let [{:keys [tab]} @state]
[react/view {:flex 1}
[react/view {:flex-direction :row :margin-bottom 8 :padding-horizontal 4}
[accounts/tab-title state :assets (i18n/label :t/wallet-assets) (= tab :assets)]
(when (seq nfts)
[accounts/tab-title state :nft (i18n/label :t/wallet-collectibles) (= tab :nft)])
[accounts/tab-title state :nft (i18n/label :t/wallet-collectibles) (= tab :nft)]
[accounts/tab-title state :history (i18n/label :t/history) (= tab :history)]]
(cond
(= tab :assets)
@ -91,18 +90,22 @@
:align-self :stretch}}]
:render-fn (accounts/render-asset (:code currency))}]
(= tab :nft)
[list/flat-list {:data nfts
:default-separator? false
:key-fn :name
:footer [react/view
{:style {:height tabs.styles/tabs-diff
:align-self :stretch}}]
:render-fn accounts/render-collectible}]
(if (seq nfts)
[list/flat-list {:data nfts
:default-separator? false
:key-fn :name
:footer [react/view
{:style {:height tabs.styles/tabs-diff
:align-self :stretch}}]
:render-fn accounts/render-collectible}]
[react/view {:align-items :center :margin-top 32}
[react/text {:style {:color colors/gray}}
(i18n/label :t/no-collectibles)]])
(= tab :history)
[transactions])])))
[transactions address])])))
(views/defview account []
(views/letsubs [{:keys [name] :as account} [:get-screen-params]]
(views/letsubs [{:keys [name address] :as account} [:get-screen-params]]
[react/view {:flex 1 :background-color colors/white}
[toolbar-view name]
[react/scroll-view
@ -110,4 +113,4 @@
[react/scroll-view {:horizontal true}
[react/view {:flex-direction :row :padding-top 8 :padding-bottom 12}
[account-card account]]]]
[assets-and-collections]]]))
[assets-and-collections address]]]))

View File

@ -50,10 +50,10 @@
(defn add-account []
[react/view
[action-button/action-button-disabled {:label (i18n/label :t/add-an-account)
:icon :main-icons/add
:icon-opts {:color :blue}
:on-press #(hide-sheet-and-dispatch [:navigate-to])}]
[action-button/action-button {:label (i18n/label :t/add-an-account)
:icon :main-icons/add
:icon-opts {:color :blue}
:on-press #(hide-sheet-and-dispatch [:navigate-to :add-new-account])}]
[action-button/action-button-disabled {:label (i18n/label :t/add-a-watch-account)
:icon :main-icons/watch
:icon-opts {:color :blue}

View File

@ -4,6 +4,7 @@
(defn card [color]
{:width 156
:height 145
:margin-right 16
:background-color color
:shadow-offset {:width 0 :height 2}
:shadow-radius 8
@ -20,7 +21,6 @@
(def add-card
{:width 156
:height 145
:margin-left 16
:margin-top 5
:margin-right 5
:margin-bottom 5

View File

@ -25,7 +25,7 @@
(views/defview account-card [{:keys [name color address] :as account}]
(views/letsubs [currency [:wallet/currency]
portfolio-value [:portfolio-value]]
portfolio-value [:account-portfolio-value address]]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:navigate-to :wallet-account account])
:on-long-press #(re-frame/dispatch [:bottom-sheet/show-sheet
{:content (fn [] [sheets/send-receive address])
@ -97,7 +97,7 @@
:accessories [items-number :chevron]}]]))
(views/defview assets-and-collections []
(views/letsubs [{:keys [tokens nfts]} [:wallet/visible-assets-with-values]
(views/letsubs [{:keys [tokens nfts]} [:wallet/all-visible-assets-with-values]
currency [:wallet/currency]]
(let [{:keys [tab]} @state]
[react/view {:flex 1}

View File

@ -0,0 +1,127 @@
(ns status-im.ui.screens.wallet.add-new.views
(:require-macros [status-im.utils.views :refer [defview letsubs]])
(:require [status-im.ui.components.react :as react]
[status-im.ui.components.status-bar.view :as status-bar]
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.i18n :as i18n]
[re-frame.core :as re-frame]
[status-im.ui.components.colors :as colors]
[status-im.ui.components.list-header.views :as list-header]
[status-im.ui.components.list-item.views :as list-item]
[status-im.ui.components.common.common :as components.common]
[status-im.ui.components.icons.vector-icons :as icons]
[status-im.ui.components.text-input.view :as text-input]
[reagent.core :as reagent]
[clojure.string :as string]
[cljs.spec.alpha :as spec]
[status-im.multiaccounts.db :as multiaccounts.db]))
(defn add-account []
[react/view {:flex 1}
[status-bar/status-bar]
[toolbar/toolbar {:transparent? true} toolbar/default-nav-back nil]
[react/scroll-view {:keyboard-should-persist-taps :handled
:style {:flex 1}}
[react/view {:align-items :center :padding-horizontal 40}
[react/text {:style {:typography :header :margin-top 16}} (i18n/label :t/add-an-account)]
[react/text {:style {:color colors/gray :text-align :center :margin-top 16 :line-height 22}}
(i18n/label :t/add-account-description)]]
[react/view {:height 52}]
[list-header/list-header (i18n/label :t/default)]
[list-item/list-item {:title (i18n/label :t/generate-a-new-account) :theme :action
:icon :main-icons/add :accessories [:chevron]
:on-press #(re-frame/dispatch [:navigate-to :add-new-account-password])}]]])
(defview colors-popover [selected-color]
(letsubs [width [:dimensions/window-width]]
[react/view {:padding-bottom 16}
[react/scroll-view {:style {:margin 16}}
(doall
(for [color colors/account-colors]
^{:key color}
[react/touchable-highlight {:on-press #(do
(re-frame/dispatch [:set-in [:generate-account :account :color] color])
(re-frame/dispatch [:hide-popover]))}
[react/view {:height 52 :background-color color :border-radius 8 :width (* 0.7 width)
:justify-content :center :padding-left 12 :margin-bottom 16}
[react/view {:height 32 :width 32 :border-radius 20 :align-items :center :justify-content :center
:background-color colors/black-transparent}
(when (= selected-color color)
[icons/icon :main-icons/check {:color colors/white}])]]]))]
[components.common/button {:on-press #(re-frame/dispatch [:hide-popover])
:label (i18n/label :t/cancel)
:background? false}]]))
(defview account-added []
(letsubs [{:keys [account]} [:generate-account]]
[react/keyboard-avoiding-view {:flex 1}
[status-bar/status-bar]
[react/scroll-view {:keyboard-should-persist-taps :handled
:style {:margin-top 70 :flex 1}}
[react/view {:align-items :center :padding-horizontal 40}
[react/view {:height 40 :width 40 :border-radius 20 :align-items :center :justify-content :center
:background-color (:color account)}
[icons/icon :main-icons/check {:color colors/white}]]
[react/text {:style {:typography :header :margin-top 16}}
(i18n/label :t/account-added)]
[react/text {:style {:color colors/gray :text-align :center :margin-top 16 :line-height 22}}
(i18n/label :t/you-can-change-account)]]
[react/view {:height 52}]
[react/view {:margin-horizontal 16}
[text-input/text-input-with-label
{:label (i18n/label :t/account-name)
:auto-focus false
:default-value (:name account)
:on-change-text #(re-frame/dispatch [:set-in [:generate-account :account :name] %])}]
[react/text {:style {:margin-top 30}} (i18n/label :t/account-color)]
[react/touchable-highlight {:on-press #(re-frame/dispatch [:show-popover {:view [colors-popover (:color account)]
:style {:max-height "60%"}}])}
[react/view {:height 52 :margin-top 12 :background-color (:color account) :border-radius 8
:align-items :flex-end :justify-content :center :padding-right 12}
[icons/icon :main-icons/dropdown {:color colors/white}]]]]]
[react/view {:style {:flex-direction :row
:justify-content :flex-end
:align-self :stretch
:padding-vertical 16
:border-top-width 1
:border-top-color colors/gray-lighter
:padding-right 12}}
[components.common/bottom-button {:label (i18n/label :t/finish)
:on-press #(re-frame/dispatch [:wallet.accounts/save-generated-account])
:disabled? (string/blank? (:name account))
:forward? true}]]]))
(defview password []
(letsubs [{:keys [error]} [:generate-account]
entered-password (reagent/atom "")]
[react/keyboard-avoiding-view {:style {:flex 1}}
[status-bar/status-bar {:flat? true}]
[toolbar/toolbar {:transparent? true} toolbar/default-nav-back nil]
[react/view {:flex 1}
[react/view {:style {:flex 1
:justify-content :space-between
:align-items :center :margin-horizontal 16}}
[react/text {:style {:typography :header :margin-top 16}} (i18n/label :t/enter-your-password)]
[react/view {:style {:justify-content :center :flex 1}}
[react/text-input {:secure-text-entry true
:auto-focus true
:text-align :center
:placeholder ""
:style {:typography :header}
:on-change-text #(reset! entered-password %)}]
(when error
[react/text {:style {:text-align :center :color colors/red :margin-top 76}} error])]
[react/text {:style {:color colors/gray :text-align :center :margin-bottom 16}}
(i18n/label :t/to-encrypt-enter-password)]]
[react/view {:style {:flex-direction :row
:justify-content :flex-end
:align-self :stretch
:padding-vertical 16
:border-top-width 1
:border-top-color colors/gray-lighter
:padding-right 12}}
[components.common/bottom-button {:label (i18n/label :t/generate-account)
:on-press #(re-frame/dispatch
[:wallet.accounts/generate-new-account @entered-password])
:disabled? (not (spec/valid? ::multiaccounts.db/password @entered-password))
:forward? true}]]]]))

View File

@ -29,7 +29,8 @@
[status-im.wallet.utils :as wallet.utils]
[status-im.utils.core :as utils.core]
[status-im.utils.money :as money]
[status-im.utils.utils :as utils.utils])
[status-im.utils.utils :as utils.utils]
[status-im.ui.components.chat-icon.screen :as chat-icon.screen])
(:require-macros [status-im.utils.views :as views]))
;; Wallet tab has a different coloring scheme (dark) that forces color changes (background, text)
@ -120,8 +121,8 @@
(wallet.utils/display-symbol token)]]
[list/item-secondary (wallet.utils/format-amount amount decimals)]]]]])
(views/defview assets [type]
(views/letsubs [assets [:wallet/transferrable-assets-with-amount]]
(views/defview assets [type address]
(views/letsubs [assets [:wallet/transferrable-assets-with-amount address]]
[simple-screen
[toolbar (i18n/label :t/wallet-assets)]
[react/view {:style (assoc components.styles/flex :background-color :white)}
@ -130,11 +131,13 @@
:key-fn (comp str :symbol)
:render-fn #(render-token % type)}]]]))
(defn send-assets []
[assets :send])
(views/defview send-assets []
(views/letsubs [address [:get-screen-params]]
[assets :send address]))
(defn request-assets []
[assets :request])
(views/defview request-assets []
(views/letsubs [address [:get-screen-params]]
[assets :request address]))
(defn- type->view [k]
(case k
@ -142,14 +145,14 @@
:request :wallet-request-assets
(throw (str "Unknown type: " k))))
(views/defview asset-selector [{:keys [disabled? type symbol error]}]
(views/letsubs [balance [:balance]
(views/defview asset-selector [{:keys [disabled? type symbol error address]}]
(views/letsubs [balance [:balance address]
chain [:ethereum/chain-keyword]
all-tokens [:wallet/all-tokens]]
(let [{:keys [name icon decimals color] :as token} (tokens/asset-for all-tokens chain symbol)]
(when name
[react/view
[cartouche {:disabled? disabled? :on-press #(re-frame/dispatch [:navigate-to (type->view type)])}
[cartouche {:disabled? disabled? :on-press #(re-frame/dispatch [:navigate-to (type->view type) address])}
(i18n/label :t/wallet-asset)
[react/view {:style styles/asset-content-container
:accessibility-label :choose-asset-button}
@ -202,6 +205,17 @@
:accessibility-label :contact-address-text}
(eip55/address->checksum (ethereum/normalized-address (:address contact)))]]]])
(defn render-account [account]
[list/touchable-item #(re-frame/dispatch [:wallet/fill-request-from-contact account false])
[list/item
[chat-icon/custom-icon-view-list (:name account) (:color account)]
[list/item-content
[list/item-primary {:accessibility-label :contact-name-text}
(:name account)]
[react/text {:style list.styles/secondary-text
:accessibility-label :contact-address-text}
(eip55/address->checksum (ethereum/normalized-address (:address account)))]]]])
(views/defview recent-recipients []
(views/letsubs [contacts [:contacts/active]
{:keys [request?]} [:get-screen-params :recent-recipients]]
@ -212,6 +226,15 @@
:key-fn :address
:render-fn #(render-contact % request?)}]]]))
(views/defview accounts []
(views/letsubs [{:keys [accounts]} [:multiaccount]]
[simple-screen
[toolbar (i18n/label :t/accounts)]
[react/view styles/recent-recipients
[list/flat-list {:data accounts
:key-fn :address
:render-fn render-account}]]]))
(defn contact-code []
(let [content (reagent/atom nil)]
(fn []
@ -252,7 +275,9 @@
[{:label (i18n/label :t/recent-recipients)
:action #(re-frame/dispatch [:navigate-to :recent-recipients {:request? request?}])}]
(when-not contact-only?))
:options [{:label (i18n/label :t/scan-qr)
:options [{:label (i18n/label :t/accounts)
:action #(re-frame/dispatch [:navigate-to :select-account])}
{:label (i18n/label :t/scan-qr)
:action request-camera-permissions}
{:label (i18n/label :t/recipient-code)
:action #(re-frame/dispatch [:navigate-to :contact-code])}]}))

View File

@ -82,12 +82,12 @@
:auto-focus false
:placeholder "18"}]]]
[react/view {:height 16}]
[text-input/text-input-with-label
{:label (i18n/label :t/balance)
:default-value (when (and balance decimals)
(wallet.utils/format-amount balance decimals))
:editable false
:placeholder (i18n/label :t/no-tokens-found)}]]
#_[text-input/text-input-with-label
{:label (i18n/label :t/balance)
:default-value (when (and balance decimals)
(wallet.utils/format-amount balance decimals))
:editable false
:placeholder (i18n/label :t/no-tokens-found)}]]
[react/view {:style {:height 1 :background-color colors/gray-lighter}}]
[react/view {:flex-direction :row
:margin-horizontal 12

View File

@ -32,3 +32,7 @@
(defmethod navigation/preload-data! :wallet-add-custom-token
[db [event]]
(dissoc db :wallet/custom-token-screen))
(defmethod navigation/preload-data! :add-new-account
[db [event]]
(dissoc db :generate-account))

View File

@ -80,7 +80,7 @@
(eip55/address->checksum address)]]]
[react/view {:height 1 :background-color colors/gray-lighter :margin-top 8}]
[react/view {:padding-bottom 16}
[components.common/button {:on-press #(re-frame/dispatch [:wallet.accounts/share])
[components.common/button {:on-press #(re-frame/dispatch [:wallet.accounts/share address])
:button-style {:margin-vertical 20 :margin-horizontal 16}
:accessibility-label :share-address-button
:label (i18n/label :t/share-address)}]

View File

@ -36,7 +36,7 @@
colors/white-light-transparent)}]]])
(defn- render-send-transaction-view [{:keys [chain transaction scroll all-tokens amount-input network-status]}]
(let [{:keys [amount amount-text amount-error asset-error to to-name sufficient-funds? symbol]} transaction
(let [{:keys [from amount amount-text amount-error asset-error to to-name sufficient-funds? symbol]} transaction
{:keys [decimals] :as token} (tokens/asset-for all-tokens chain symbol)
online? (= :online network-status)]
[wallet.components/simple-screen {:avoid-keyboard? true
@ -54,6 +54,7 @@
:name to-name}]
[wallet.components/asset-selector
{:error asset-error
:address from
:type :send
:symbol symbol}]
[wallet.components/amount-selector

View File

@ -34,6 +34,7 @@
(def empty-text
{:text-align :center
:color colors/gray
:margin-top 22
:margin-horizontal 92})

View File

@ -11,17 +11,14 @@
[status-im.ui.screens.wallet.transactions.styles :as styles])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TRANSACTION HISTORY
;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn history-action
[all-filters?]
(cond->
{:icon :main-icons/filter
:icon-opts {:accessibility-label :filters-button}
:handler #(re-frame/dispatch [:navigate-to :wallet-transactions-filter])}
(not all-filters?) (assoc-in [:icon-opts :overlay-style] styles/corner-dot)))
(cond-> {:icon :main-icons/filter
:icon-opts {:accessibility-label :filters-button}
:handler #(re-frame/dispatch [:navigate-to :wallet-transactions-filter])}
(not all-filters?)
(assoc-in [:icon-opts :overlay-style] styles/corner-dot)))
(defn- toolbar-view
[all-filters?]
@ -102,19 +99,6 @@
:key :transactions-history-empty}]
:refreshing false}]])
(defview transactions
[]
(letsubs [{:keys [transaction-history-sections all-filters?]}
[:wallet.transactions.history/screen]]
[react/view styles/transactions-view
[status-bar/status-bar]
[toolbar-view all-filters?]
[history-list transaction-history-sections]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TRANSACTION FILTERS
;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn- render-item-filter [{:keys [id label checked? on-touch]}]
[react/view {:accessibility-label :filter-item}
[list/list-item-with-checkbox
@ -151,10 +135,6 @@
:data filters}]
:key-fn :id}]]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TRANSACTION DETAILS
;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn details-header
[date type amount-text currency-text]
[react/view {:style styles/details-header}
@ -237,11 +217,11 @@
{:label (i18n/label :t/open-on-etherscan)
:action #(.openURL (react/linking) url)}])])
(defview transaction-details []
(letsubs [{:keys [hash url type confirmations confirmations-progress
(defview transaction-details-view [hash address]
(letsubs [{:keys [url type confirmations confirmations-progress
date amount-text currency-text]
:as transaction}
[:wallet.transactions.details/screen]]
[:wallet.transactions.details/screen hash address]]
[react/view {:style components.styles/flex}
[status-bar/status-bar]
[toolbar/toolbar {}
@ -253,3 +233,8 @@
[details-confirmations confirmations confirmations-progress (= :failed type)]
[react/view {:style styles/details-separator}]
[details-list transaction]]]))
(defview transaction-details []
(letsubs [{:keys [hash address]} [:get-screen-params]]
(when (and hash address)
[transaction-details-view hash address])))

View File

@ -4,14 +4,67 @@
[status-im.utils.fx :as fx]
[status-im.ethereum.eip55 :as eip55]
[status-im.ui.components.list-selection :as list-selection]
[status-im.utils.handlers :as handlers]))
[status-im.utils.handlers :as handlers]
[status-im.native-module.core :as status]
[status-im.utils.types :as types]
[status-im.constants :as constants]
[status-im.ui.components.colors :as colors]
[status-im.ui.screens.navigation :as navigation]
[status-im.multiaccounts.update.core :as multiaccounts.update]
[status-im.wallet.core :as wallet]
[status-im.i18n :as i18n]))
(re-frame/reg-fx
:list.selection/open-share
(fn [obj]
(list-selection/open-share obj)))
(re-frame/reg-fx
:wallet.accounts/generate-account
(fn [{:keys [address password path-num]}]
(status/multiaccount-load-account
address
password
(fn [value]
(let [{:keys [id error]} (types/json->clj value)]
(if error
(re-frame/dispatch [:set-in [:generate-account :error] (i18n/label :t/add-account-incorrect-password)])
(let [path (str constants/path-root "/" path-num)]
(status/multiaccount-derive-addresses
id
[path]
#(re-frame/dispatch [:wallet.accounts/account-generated
(merge
(get (types/json->clj %) (keyword path))
{:name (str "Account " path-num)
:color (rand-nth colors/account-colors)})])))))))))
(fx/defn set-symbol-request
{:events [:wallet.accounts/share]}
[{:keys [db]}]
{:list.selection/open-share {:message (eip55/address->checksum (ethereum/default-address db))}})
[_ address]
{:list.selection/open-share {:message (eip55/address->checksum address)}})
(fx/defn generate-new-account
{:events [:wallet.accounts/generate-new-account]}
[{:keys [db]} password]
{:wallet.accounts/generate-account {:address (get-in db [:multiaccount :address])
:path-num (inc (get-in db [:multiaccount :latest-derived-path]))
:password password}})
(fx/defn account-generated
{:events [:wallet.accounts/account-generated]}
[{:keys [db] :as cofx} account]
(fx/merge cofx
{:db (assoc db :generate-account {:account account})}
(navigation/navigate-to-cofx :account-added nil)))
(fx/defn save-account
{:events [:wallet.accounts/save-generated-account]}
[{:keys [db] :as cofx}]
(let [new-account (get-in db [:generate-account :account])
{:keys [accounts latest-derived-path]} (:multiaccount db)]
(fx/merge cofx
(multiaccounts.update/multiaccount-update {:accounts (conj accounts new-account)
:latest-derived-path (inc latest-derived-path)} nil)
(wallet/update-balances nil)
(navigation/navigate-to-cofx :wallet nil))))

View File

@ -29,6 +29,16 @@
:on-success on-success
:on-error on-error})))
(re-frame/reg-fx
:wallet/get-balances
(fn [addresses]
(doseq [address addresses]
(json-rpc/call
{:method "eth_getBalance"
:params [address "latest"]
:on-success #(re-frame/dispatch [:wallet.callback/update-balance-success address %])
:on-error #(re-frame/dispatch [:wallet.callback/update-balance-fail %])}))))
;; TODO(oskarth): At some point we want to get list of relevant
;; assets to get prices for
(re-frame/reg-fx
@ -55,21 +65,17 @@
[{:keys [db]} err]
(log/debug "Unable to get balance: " err)
{:db (-> db
(assoc-error-message :balance-update :error-unable-to-get-balance)
(assoc-in [:wallet :balance-loading?] false))})
(assoc-error-message :balance-update :error-unable-to-get-balance))})
(fx/defn on-update-token-balance-fail
[{:keys [db]} symbol err]
(log/debug "Unable to get token " symbol "balance: " err)
{:db (-> db
(assoc-error-message :balance-update :error-unable-to-get-token-balance)
(assoc-in [:wallet :balance-loading?] false))})
(assoc-error-message :balance-update :error-unable-to-get-token-balance))})
(fx/defn open-transaction-details
[{:keys [db] :as cofx} hash]
(fx/merge cofx
{:db (assoc-in db [:wallet :current-transaction] hash)}
(navigation/navigate-to-cofx :wallet-transaction-details nil)))
[{:keys [db] :as cofx} hash address]
(navigation/navigate-to-cofx cofx :wallet-transaction-details {:hash hash :address address}))
(defn- validate-token-name!
[{:keys [address symbol name]}]
@ -136,18 +142,23 @@
(validate-token-name! token))))
(re-frame/reg-fx
:wallet/get-tokens-balance
(fn [{:keys [account-address tokens on-success on-error]}]
:wallet/get-tokens-balances
(fn [{:keys [addresses tokens assets]}]
;;TODO not great to have so many calls , should be optimized, there is wallet_getTokensBalances why wouldn't use it?
(doseq [{:keys [address symbol]} tokens]
(json-rpc/eth-call
{:contract address
:method "balanceOf(address)"
:params [account-address]
:outputs ["uint256"]
:on-success
(fn [[balance]]
(on-success symbol (money/bignumber balance)))
:on-error #(on-error symbol %)}))))
(doseq [account-address addresses]
(json-rpc/eth-call
{:contract address
:method "balanceOf(address)"
:params [account-address]
:outputs ["uint256"]
:on-success (fn [[balance]]
(if (and assets (assets symbol))
(re-frame/dispatch [:wallet.callback/update-token-balance-success account-address symbol balance])
;; NOTE: when there it is not a visible assets we make an initialization round
(when (pos? balance)
(re-frame/dispatch [:wallet/token-found account-address symbol balance]))))
:on-error #(re-frame/dispatch [:wallet.callback/update-token-balance-fail symbol %])})))))
(defn clear-error-message [db error-type]
(update-in db [:wallet :errors] dissoc error-type))
@ -175,46 +186,22 @@
{:wallet/validate-tokens (get tokens/all-default-tokens chain)})))))
(fx/defn update-balances
[{{:keys [network-status :wallet/all-tokens]
{:keys [settings]} :multiaccount :as db} :db :as cofx}]
(let [normalized-address (ethereum/current-address db)
chain (ethereum/chain-keyword db)
assets (get-in settings [:wallet :visible-tokens chain])
tokens (->> (tokens/tokens-for all-tokens chain)
(remove #(or (:hidden? %))))]
[{{:keys [network-status :wallet/all-tokens]
{:keys [settings accounts]} :multiaccount :as db} :db :as cofx} addresses]
(let [addresses (or addresses (map :address accounts))
chain (ethereum/chain-keyword db)
assets (get-in settings [:wallet :visible-tokens chain])
tokens (->> (tokens/tokens-for all-tokens chain)
(remove #(or (:hidden? %))))]
(when (not= network-status :offline)
(fx/merge
cofx
{:wallet/get-balance
{:account-address normalized-address
:on-success #(re-frame/dispatch
[:wallet.callback/update-balance-success %])
:on-error #(re-frame/dispatch
[:wallet.callback/update-balance-fail %])}
:wallet/get-tokens-balance
{:account-address normalized-address
:tokens tokens
:on-success
(fn [symbol balance]
(if (and assets
(assets symbol))
(re-frame/dispatch
[:wallet.callback/update-token-balance-success symbol balance])
;; NOTE: when there it is not a visible assets
;; we make an initialization round
(when (> balance 0)
(re-frame/dispatch
[:wallet/token-found symbol balance]))))
:on-error
(fn [symbol error]
(re-frame/dispatch
[:wallet.callback/update-token-balance-fail symbol error]))}
:db
(-> db
(clear-error-message :balance-update)
(assoc-in [:wallet :balance-loading?] true))}
{:wallet/get-balances addresses
:wallet/get-tokens-balances {:addresses addresses
:assets assets
:tokens tokens}
:db (-> db
(clear-error-message :balance-update))}
(when-not assets
(multiaccounts.update/update-settings
(assoc-in settings
@ -260,16 +247,14 @@
:prices-loading? false)})
(fx/defn update-balance
[{:keys [db]} balance]
[{:keys [db]} address balance]
{:db (-> db
(assoc-in [:wallet :balance :ETH] (money/bignumber balance))
(assoc-in [:wallet :balance-loading?] false))})
(assoc-in [:wallet :accounts address :balance :ETH] (money/bignumber balance)))})
(fx/defn update-token-balance
[{:keys [db]} symbol balance]
[{:keys [db]} address symbol balance]
{:db (-> db
(assoc-in [:wallet :balance symbol] (money/bignumber balance))
(assoc-in [:wallet :balance-loading?] false))})
(assoc-in [:wallet :accounts address :balance symbol] (money/bignumber balance)))})
(defn update-toggle-in-settings
[{{:keys [multiaccount] :as db} :db} symbol checked?]
@ -297,11 +282,11 @@
(multiaccounts.update/update-settings cofx new-settings {})))
(fx/defn configure-token-balance-and-visibility
[cofx symbol balance]
[cofx address symbol balance]
(fx/merge cofx
(toggle-visible-token symbol true)
;;TODO(goranjovic): move `update-token-balance-success` function to wallet models
(update-token-balance symbol balance)))
(update-token-balance address symbol balance)))
(defn set-and-validate-amount-db [db amount symbol decimals]
(let [{:keys [value error]} (wallet.db/parse-amount amount decimals)]

View File

@ -91,8 +91,7 @@
(fx/defn total-supply-result
[{:keys [db]} contract total-supply]
(if (money/valid? total-supply)
{:wallet.custom-token/get-balance
[contract (ethereum/default-address db)]}
{:wallet.custom-token/get-name contract}
{:db (update db
:wallet/custom-token-screen
merge {:in-progress? nil

View File

@ -66,8 +66,7 @@
#{:inbound :outbound :pending :failed})
(def default-wallet
{:filters default-wallet-filters
:transactions empty-transaction-map})
{:filters default-wallet-filters})
(defn get-confirmations
[{:keys [block]} current-block]

View File

@ -26,7 +26,8 @@
(testing "first tx object is parsed"
(is (= (dissoc (get-in sign-first [:db :signing/tx]) :token)
(merge first-tx
{:gas nil
{:from {:address nil}
:gas nil
:gasPrice nil
:data nil
:to to
@ -49,7 +50,8 @@
(testing "second tx object is parsed"
(is (= (dissoc (get-in first-discarded [:db :signing/tx]) :token)
(merge second-tx
{:gas nil
{:from {:address nil}
:gas nil
:gasPrice nil
:data data
:to contract

View File

@ -173,7 +173,7 @@
"intro-wizard-text7": "Status will notify you about new messages. You can edit your notification preferences later in settings",
"generate-a-key": "Generate a key",
"generate-a-new-key": "Generate a new key",
"enter-your-password": "Enter your password...",
"enter-your-password": "Enter your password",
"generating-keys": "Generating keys...",
"you-will-need-this-code": "You'll need this code to open Status and sign transactions",
"this-device": "This device",
@ -1259,5 +1259,15 @@
"not-keycard-title": "Not a Keycard",
"not-keycard-text": "The card you used is not a Keycard. You need to purchase a Keycard to use it",
"create-new-key": "Create a new key",
"add-another-key": "Add another key"
"add-another-key": "Add another key",
"generate-a-new-account": "Generate a new account",
"generate-account": "Generate account",
"add-account-description": "You can import any type of Ethereum account to add it to your Status wallet",
"account-added": "Account added",
"you-can-change-account": "You can change the account name and color to what you wish",
"account-name": "Account name",
"account-color": "Account color",
"to-encrypt-enter-password": "To encrypt the account please enter your password",
"accounts": "Accounts",
"add-account-incorrect-password": "Password seems to be incorrect. Enter the password you use to unlock the app."
}