[#9203] Transfers fetching with less requests

- all messages are not shown right away, in order to paginate history
  a user has to press "load more" button
- added link to etherscan before transfers list
- there is a new "fetch more" button at the end of the list
- rest of changes can be found here status-im/status-go#1775
This commit is contained in:
Roman Volosovskyi 2020-01-02 12:01:58 +02:00
parent bddf3fa341
commit 161042066d
No known key found for this signature in database
GPG Key ID: 0238A4B5ECEE70DE
11 changed files with 332 additions and 76 deletions

View File

@ -44,12 +44,13 @@
(def system "system")
(def mainnet-networks
[{:id "mainnet_rpc",
:name "Mainnet with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :mainnet)
:DataDir "/ethereum/mainnet_rpc"
:UpstreamConfig {:Enabled true
:URL "https://mainnet.infura.io/v3/f315575765b14720b32382a61a89341a"}}}])
[{:id "mainnet_rpc",
:etherscan-link "https://etherscan.io/address/",
:name "Mainnet with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :mainnet)
:DataDir "/ethereum/mainnet_rpc"
:UpstreamConfig {:Enabled true
:URL "https://mainnet.infura.io/v3/f315575765b14720b32382a61a89341a"}}}])
(def sidechain-networks
[{:id "xdai_rpc",
@ -66,28 +67,37 @@
:URL "https://core.poa.network"}}}])
(def testnet-networks
[{:id "testnet_rpc",
:name "Ropsten with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :testnet)
:DataDir "/ethereum/testnet_rpc"
:UpstreamConfig {:Enabled true
:URL "https://ropsten.infura.io/v3/f315575765b14720b32382a61a89341a"}}}
{:id "rinkeby_rpc",
:name "Rinkeby with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :rinkeby)
:DataDir "/ethereum/rinkeby_rpc"
:UpstreamConfig {:Enabled true
:URL "https://rinkeby.infura.io/v3/f315575765b14720b32382a61a89341a"}}}
{:id "goerli_rpc",
:name "Goerli with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :goerli)
:DataDir "/ethereum/goerli_rpc"
:UpstreamConfig {:Enabled true
:URL "https://goerli.blockscout.com/"}}}])
[{:id "testnet_rpc",
:etherscan-link "https://ropsten.etherscan.io/address/",
:name "Ropsten with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :testnet)
:DataDir "/ethereum/testnet_rpc"
:UpstreamConfig {:Enabled true
:URL "https://ropsten.infura.io/v3/f315575765b14720b32382a61a89341a"}}}
{:id "rinkeby_rpc",
:etherscan-link "https://rinkeby.etherscan.io/address/",
:name "Rinkeby with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :rinkeby)
:DataDir "/ethereum/rinkeby_rpc"
:UpstreamConfig {:Enabled true
:URL "https://rinkeby.infura.io/v3/f315575765b14720b32382a61a89341a"}}}
{:id "goerli_rpc",
:etherscan-link "https://goerli.etherscan.io/address/",
:name "Goerli with upstream RPC",
:config {:NetworkId (ethereum/chain-keyword->chain-id :goerli)
:DataDir "/ethereum/goerli_rpc"
:UpstreamConfig {:Enabled true
:URL "https://goerli.blockscout.com/"}}}])
(def default-networks
(concat testnet-networks mainnet-networks sidechain-networks))
(def default-networks-by-id
(into {}
(map (fn [{:keys [id] :as network}]
[id network])
default-networks)))
(def default-multiaccount
{:preview-privacy? config/blank-preview?
:wallet/visible-tokens {:mainnet #{:SNT}}

View File

@ -21,6 +21,7 @@
(update :timestamp decode/uint))}
"eth_getTransactionByHash" {}
"eth_getTransactionReceipt" {}
"eth_getBlockByNumber" {}
"eth_newBlockFilter" {:subscription? true}
"eth_newFilter" {:subscription? true}
"eth_syncing" {}
@ -80,6 +81,9 @@
"status_chats" {}
"wallet_getTransfers" {}
"wallet_getTokensBalances" {}
"wallet_getBlocksByAddress" {}
"wallet_getTransfersFromBlock" {}
"wallet_getTransfersByAddress" {}
"wallet_getCustomTokens" {}
"wallet_addCustomToken" {}
"wallet_deleteCustomToken" {}

View File

@ -1,6 +1,7 @@
(ns status-im.ethereum.subscriptions
(:require [re-frame.core :as re-frame]
[status-im.constants :as constants]
[status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.ethereum.tokens :as tokens]
@ -29,6 +30,9 @@
chain (ethereum/chain-keyword db)
chain-tokens (into {} (map (juxt :address identity)
(tokens/tokens-for all-tokens chain)))]
(log/debug "[wallet-subs] new-block"
"accounts" accounts
"block" block-number)
(fx/merge cofx
(cond-> {}
(not historical?)
@ -37,26 +41,63 @@
;;NOTE only get transfers if the new block contains some
;; from/to one of the multiaccount accounts
(not-empty accounts)
(assoc ::transactions/get-transfers {:chain-tokens chain-tokens
:from-block block-number
:historical? historical?}))
(assoc :transactions/get-transfers-from-block
{:chain-tokens chain-tokens
:addresses accounts
:block block-number
:historical? historical?}))
(transactions/check-watched-transactions))))
(fx/defn reorg
[{:keys [db] :as cofx} block-number accounts]
[{:keys [db] :as cofx} {:keys [blockNumber accounts]}]
(log/debug "[wallet-subs] reorg"
"accounts" accounts
"block-number" blockNumber)
{:db (update-in db [:wallet :transactions]
wallet/remove-transactions-since-block blockNumber)})
(fx/defn recent-history-fetching-started
[{:keys [db]} accounts]
(log/debug "[wallet-subs] recent-history-fetching-started"
"accounts" accounts)
{:db (transactions/update-fetching-status db accounts :recent? true)})
(fx/defn recent-history-fetching-ended
[{:keys [db] :as cofx} {:keys [accounts blockNumber]}]
(log/debug "[wallet-subs] recent-history-fetching-ended"
"accounts" accounts
"block" blockNumber)
(let [{:keys [:wallet/all-tokens]} db
chain (ethereum/chain-keyword db)
chain-tokens (into {} (map (juxt :address identity)
(tokens/tokens-for all-tokens chain)))]
{:db (update-in db [:wallet :accounts]
wallet/remove-transactions-since-block block-number)
::transactions/get-transfers {:chain-tokens chain-tokens
:from-block block-number}}))
{:db (-> db
(update-in [:wallet :accounts]
wallet/remove-transactions-since-block blockNumber)
(transactions/update-fetching-status accounts :recent? false))
:transactions/get-transfers
{:chain-tokens chain-tokens
:addresses (reduce
(fn [v address]
(let [normalized-address
(eip55/address->checksum address)]
(if (contains? v normalized-address)
v
(conj v address))))
[]
accounts)
:before-block blockNumber
:page-size 20
:historical? true}}))
(fx/defn new-wallet-event
[{:keys [db] :as cofx} {:keys [type blockNumber accounts] :as event}]
[cofx {:keys [type blockNumber accounts] :as event}]
(log/debug "[wallet-subs] new-wallet-event"
"event-type" type)
(case type
"newblock" (new-block cofx false blockNumber accounts)
"history" (new-block cofx true blockNumber accounts)
"reorg" (reorg cofx blockNumber accounts)
"reorg" (reorg cofx event)
"recent-history-fetching" (recent-history-fetching-started cofx accounts)
"recent-history-ready" (recent-history-fetching-ended cofx event)
(log/warn ::unknown-wallet-event :type type :event event)))

View File

@ -6,11 +6,11 @@
[status-im.ethereum.encode :as encode]
[status-im.ethereum.json-rpc :as json-rpc]
[status-im.ethereum.core :as ethereum]
[status-im.ethereum.eip55 :as eip55]
[status-im.ethereum.tokens :as tokens]
[status-im.utils.fx :as fx]
[status-im.utils.money :as money]
[status-im.wallet.core :as wallet]))
[status-im.wallet.core :as wallet]
[taoensso.timbre :as log]))
(def confirmations-count-threshold 12)
@ -130,10 +130,8 @@
transaction can contain multiple transfers and they would overwrite each other
in the transfer map if identified by hash"
[{:keys [db] :as cofx} {:keys [hash id address] :as transfer}]
(let [transfer-by-hash (get-in db [:wallet :accounts address :transactions hash])
transfer-by-id (get-in db [:wallet :accounts address :transactions id])]
(when-let [unique-id (when-not (or transfer-by-id
(= transfer transfer-by-hash))
(let [transfer-by-hash (get-in db [:wallet :accounts address :transactions hash])]
(when-let [unique-id (when-not (= transfer transfer-by-hash)
(if (and transfer-by-hash
(not (= :pending
(:type transfer-by-hash))))
@ -144,32 +142,146 @@
(assoc transfer :hash unique-id))}
(check-transaction transfer)))))
(defn get-min-known-block [db address]
(get-in db [:wallet :accounts (eip55/address->checksum address) :min-block]))
(fx/defn set-lowest-fetched-block
[{:keys [db]} address transfers]
(let [min-block (reduce
(fn [min-block {:keys [block]}]
(min (or min-block block) block))
(get-min-known-block db address)
transfers)]
{:db (assoc-in
db
[:wallet :accounts (eip55/address->checksum address) :min-block]
min-block)}))
(defn update-fetching-status
[db addresses fetching-type state]
(update-in
db [:wallet :fetching]
(fn [accounts]
(reduce
(fn [accounts address]
(assoc-in accounts
[(eip55/address->checksum address) fetching-type]
state))
accounts
addresses))))
(fx/defn tx-fetching-in-progress
[{:keys [db]} addresses]
{:db (update-fetching-status db addresses :history? true)})
(fx/defn tx-fetching-ended
[{:keys [db]} addresses]
{:db (update-fetching-status db addresses :history? false)})
(fx/defn tx-history-end-reached
[{:keys [db]} address]
{:db (assoc-in db [:wallet :fetching address :all-fetched?] true)})
(fx/defn new-transfers
{:events [::new-transfers]}
[{:keys [db] :as cofx} transfers historical?]
(let [effects (cond-> (map add-transfer transfers)
;;NOTE: we only update the balance for new transfers and not historical ones
(not historical?) (conj (wallet/update-balances (into [] (reduce (fn [acc {:keys [address]}]
(conj acc address))
#{}
transfers)))))]
(apply fx/merge cofx effects)))
[{:keys [db] :as cofx} transfers {:keys [address historical? before-block]}]
(let [min-block (get-in db [:wallet :accounts address :min-block])
effects (cond-> [(when (seq transfers)
(set-lowest-fetched-block address transfers))]
(seq transfers)
(concat (mapv add-transfer transfers))
;;NOTE: we only update the balance for new transfers and not
;; historical ones
(not historical?)
(conj (wallet/update-balances
(into [] (reduce (fn [acc {:keys [address]}]
(conj acc address))
#{}
transfers))))
(and (= min-block before-block)
(<= (count transfers) 1))
(conj (tx-history-end-reached address)))]
(apply fx/merge cofx (tx-fetching-ended [address]) effects)))
(fx/defn tx-fetching-failed
{:events [::tx-fetching-failed]}
[cofx error address]
(log/debug "[transactions] tx-fetching-failed"
"address" address
"error" error)
(tx-fetching-ended cofx [address]))
(re-frame/reg-fx
::get-transfers
(fn [{:keys [chain-tokens from-block to-block historical?]
:or {from-block "0"
to-block nil}}]
(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 %) historical?])})))
:transactions/get-transfers-from-block
(fn [{:keys [chain-tokens addresses block] :as params}]
(log/debug "[transactions] get-transfers-from-block"
"addresses" addresses
"block" block)
(doseq [address addresses]
(json-rpc/call
{:method "wallet_getTransfersFromBlock"
:params [address (encode/uint block)]
:on-success #(re-frame/dispatch
[::new-transfers
(enrich-transfers chain-tokens %)
(assoc params :address address)])
:on-error #(re-frame/dispatch [::tx-fetching-failed % address])}))))
(re-frame/reg-fx
:transactions/get-transfers
(fn [{:keys [chain-tokens addresses before-block page-size]
:as params
:or {page-size 0}}]
{:pre [(cljs.spec.alpha/valid?
(cljs.spec.alpha/coll-of string?)
addresses)]}
(log/debug "[transactions] get-transfers"
"addresses" addresses
"block" before-block
"page-size" page-size)
(when before-block
(doseq [address addresses]
(json-rpc/call
{:method "wallet_getTransfersByAddress"
:params [address (encode/uint before-block) (encode/uint page-size)]
:on-success #(re-frame/dispatch
[::new-transfers
(enrich-transfers chain-tokens %)
(assoc params :address address)])
:on-error #(re-frame/dispatch [::tx-fetching-failed address])})))))
(fx/defn initialize
[{:keys [db] :as cofx}]
[{:keys [db]} addresses]
(let [{: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
:historical? true}}))
{:transactions/get-transfers
{:chain-tokens chain-tokens
:addresses (map eip55/address->checksum addresses)
:page-size 20
:historical? true}}))
(fx/defn fetch-more-tx
{:events [:transactions/fetch-more]}
[{:keys [db] :as cofx} address]
(let [all-tokens (:wallet/all-tokens db)
chain (ethereum/chain-keyword db)
chain-tokens (into
{}
(map (juxt :address identity)
(tokens/tokens-for
all-tokens chain)))
min-known-block (or (get-min-known-block db address)
(:ethereum/current-block db))]
(fx/merge
cofx
{:transactions/get-transfers
{:chain-tokens chain-tokens
:addresses [address]
:before-block min-known-block
:page-size 20
:historical? true}}
(tx-fetching-in-progress [address]))))

View File

@ -89,7 +89,10 @@
(wallet/initialize-tokens custom-tokens)
(wallet/update-balances nil)
(wallet/update-prices)
(transactions/initialize)))
(transactions/initialize
(->> accounts
(filter :wallet)
(map :address)))))
(fx/defn login
{:events [:multiaccounts.login.ui/password-input-submitted]}

View File

@ -6,7 +6,8 @@
[status-im.utils.config :as config]
[status-im.utils.fx :as fx]
[status-im.utils.platform :as utils.platform]
[status-im.utils.types :as types])
[status-im.utils.types :as types]
[taoensso.timbre :as log])
(:require-macros [status-im.utils.slurp :refer [slurp]]))
(defn- add-custom-bootnodes [config network all-bootnodes]

View File

@ -1174,6 +1174,33 @@
(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/fetching-tx-history?
:<- [:wallet]
(fn [wallet [_ address]]
(get-in wallet [:fetching address :history?])))
(re-frame/reg-sub
:wallet/fetching-recent-tx-history?
:<- [:wallet]
(fn [wallet [_ address]]
(get-in wallet [:fetching address :recent?])))
(re-frame/reg-sub
:wallet/tx-history-fetched?
:<- [:wallet]
(fn [wallet [_ address]]
(get-in wallet [:fetching address :all-fetched?])))
(re-frame/reg-sub
:wallet/etherscan-link
(fn [db [_ address]]
(let [network (:networks/current-network db)
link (get-in constants/default-networks-by-id
[network :etherscan-link])]
(when link
(str link address)))))
(re-frame/reg-sub
:wallet/error-message
:<- [:wallet]

View File

@ -89,7 +89,7 @@
(views/defview transactions [address]
(views/letsubs [{:keys [transaction-history-sections]}
[:wallet.transactions.history/screen address]]
[history/history-list transaction-history-sections]))
[history/history-list transaction-history-sections address]))
(views/defview assets-and-collections [address]
(views/letsubs [{:keys [tokens nfts]} [:wallet/visible-assets-with-values address]

View File

@ -4,6 +4,7 @@
[status-im.ui.components.colors :as colors]
[status-im.ui.components.list.views :as list]
[status-im.ui.components.react :as react]
[status-im.ui.components.icons.vector-icons :as vector-icons]
[status-im.ui.components.styles :as components.styles]
[status-im.ui.components.toolbar.actions :as actions]
[status-im.ui.components.toolbar.view :as toolbar]
@ -71,16 +72,71 @@
:icon-opts (merge styles/forward
{:accessibility-label :show-transaction-button})}]]]])
(defn etherscan-link [address]
(let [link @(re-frame/subscribe [:wallet/etherscan-link address])]
[react/touchable-highlight
{:on-press #(when link
(.openURL react/linking link))}
[react/view
{:style {:flex 1
:padding-horizontal 14
:flex-direction :row
:align-items :center
:background-color colors/blue-light
:height 52}}
[vector-icons/tiny-icon
:tiny-icons/tiny-external
{:color colors/blue
:container-style {:margin-right 5}}]
[react/text
{:style {:marging-left 10
:color colors/blue}}
(i18n/label :t/check-on-etherscan)]]]))
(defn history-list
[transactions-history-sections]
[react/view components.styles/flex
[list/section-list {:sections transactions-history-sections
:key-fn :hash
:render-fn #(render-transaction %)
:empty-component
[react/i18n-text {:style styles/empty-text
:key :transactions-history-empty}]
:refreshing false}]])
[transactions-history-sections address]
(let [fetching-recent-history? @(re-frame/subscribe [:wallet/fetching-recent-tx-history? address])
fetching-more-history? @(re-frame/subscribe [:wallet/fetching-tx-history? address])
all-fetched? @(re-frame/subscribe [:wallet/tx-history-fetched? address])]
[react/view components.styles/flex
[etherscan-link address]
(when fetching-recent-history?
[react/view
{:style {:flex 1
:height 40
:margin-vertical 16}}
[react/activity-indicator {:size :large
:animating true}]])
[list/section-list
{:sections transactions-history-sections
:key-fn :hash
:render-fn #(render-transaction %)
:empty-component
[react/i18n-text {:style styles/empty-text
:key :transactions-history-empty}]
:refreshing false}]
(when (and (not fetching-recent-history?)
(not all-fetched?))
(if fetching-more-history?
[react/view
{:style {:flex 1
:height 40
:margin-vertical 8}}
[react/activity-indicator {:size :large
:animating true}]]
[react/view
{:style {:flex 1
:padding-horizontal 14
:height 52
:align-items :center
:justify-content :center
:background-color colors/blue-light}}
[react/text
{:style {:color colors/blue}
:on-press (when-not fetching-more-history?
#(re-frame/dispatch
[:transactions/fetch-more address]))}
(i18n/label :t/transactions-load-more)]]))]))
(defn- render-item-filter [{:keys [id label checked? on-touch]}]
[react/view {:accessibility-label :filter-item}
@ -213,4 +269,4 @@
(defview transaction-details []
(letsubs [{:keys [hash address]} [:get-screen-params]]
(when (and hash address)
[transaction-details-view hash address])))
[transaction-details-view hash address])))

View File

@ -2,7 +2,7 @@
"_comment": "DO NOT EDIT THIS FILE BY HAND. USE 'scripts/update-status-go.sh <tag>' instead",
"owner": "status-im",
"repo": "status-go",
"version": "v0.39.11",
"commit-sha1": "a4f88d001768d2934f1ddbd75d757c52e823accb",
"src-sha256": "0vai6sdcf930r7zp2pmr3b5b2402xmvxn8087lfb34q4hwgscw3j"
"version": "v0.40.0",
"commit-sha1": "dc31c818fce6dbff6cfd76da09d901b0df742e2e",
"src-sha256": "1f0fp5c4lqnh244wcvbnl5i4p9fb0f3i0rjjvbiqwkz9zwwp3xlv"
}

View File

@ -1160,5 +1160,7 @@
"delete-account": "Delete account",
"watch-only": "Watch-only",
"cant-report-bug": "Can't report a bug",
"mail-should-be-configured": "Mail client should be configured"
"mail-should-be-configured": "Mail client should be configured",
"check-on-etherscan": "Check on etherscan",
"transactions-load-more": "Load more"
}