Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f248265182 | ||
|
|
42a2b86723 | ||
|
|
a3962654e4 |
@@ -1,41 +1,92 @@
|
||||
(ns status-im.contexts.wallet.tokens.data)
|
||||
(ns status-im.contexts.wallet.tokens.data
|
||||
(:require [camel-snake-kebab.extras :as cske]
|
||||
[clojure.set :as set]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(defn- tokens-by-key
|
||||
[{:keys [key-fn added-tokens source-name tokens chain-ids]}]
|
||||
(reduce
|
||||
(fn [acc {:keys [address chainId decimals name image verified] :as token}]
|
||||
(if (some #{chainId} chain-ids)
|
||||
(let [k (key-fn token)]
|
||||
(assoc acc
|
||||
k
|
||||
{:key k
|
||||
:name name
|
||||
:symbol (:symbol token)
|
||||
:sources (if-let [added-token (get added-tokens k)]
|
||||
(conj (:sources added-token) source-name)
|
||||
[source-name])
|
||||
:chain-id chainId
|
||||
:address address
|
||||
:decimals decimals
|
||||
:image image
|
||||
:type (if (= name "native") :native :erc20)
|
||||
:community-id (get-in token [:communityData :id])
|
||||
:verified? verified}))
|
||||
acc))
|
||||
(defn- normalize-by-symbol
|
||||
[token-source]
|
||||
(->> token-source
|
||||
:tokens
|
||||
(reduce (fn [acc token-data]
|
||||
(let [chain-id (:chainId token-data)
|
||||
token-symbol (-> token-data :symbol keyword)]
|
||||
(->> token-data
|
||||
(cske/transform-keys transforms/->kebab-case-keyword)
|
||||
(update acc chain-id assoc token-symbol))))
|
||||
{})))
|
||||
|
||||
(defn- flat-symbol-list
|
||||
[token-source]
|
||||
(->> token-source
|
||||
:tokens
|
||||
(reduce (fn [acc token-data]
|
||||
(let [chain-id (:chainId token-data)
|
||||
token-symbol (-> token-data :symbol keyword)]
|
||||
(update acc chain-id set/union #{token-symbol})))
|
||||
{})))
|
||||
|
||||
(defn- sort-symbol-list
|
||||
[symbol-map]
|
||||
(reduce (fn [acc [chain-id symbol-list]]
|
||||
(assoc acc chain-id (-> symbol-list sort vec)))
|
||||
{}
|
||||
symbol-map))
|
||||
|
||||
(defn normalize-tokens
|
||||
[{:keys [data]}]
|
||||
(-> (reduce
|
||||
(fn [{:keys [sources tokens-by-symbol symbol-lists]} token-source]
|
||||
(let [{:keys [source version name]} token-source]
|
||||
{:sources (conj sources
|
||||
{:name name
|
||||
:source source
|
||||
:version version})
|
||||
:tokens-by-symbol (merge-with merge tokens-by-symbol (normalize-by-symbol token-source))
|
||||
:symbol-lists (merge-with set/union symbol-lists (flat-symbol-list token-source))}))
|
||||
{}
|
||||
data)
|
||||
(update :symbol-lists sort-symbol-list)))
|
||||
|
||||
(defn all-token-symbols
|
||||
[symbol-lists]
|
||||
(->> symbol-lists
|
||||
vals
|
||||
(map set)
|
||||
(apply set/union)
|
||||
vec))
|
||||
|
||||
(defn normalize-market-values
|
||||
[market-values]
|
||||
(reduce-kv (fn [acc token-symbol data]
|
||||
(assoc acc
|
||||
token-symbol
|
||||
{:market-cap (:MKTCAP data)
|
||||
:high-day (:HIGHDAY data)
|
||||
:low-day (:LOWDAY data)
|
||||
:change-pct-hour (:CHANGEPCTHOUR data)
|
||||
:change-pct-day (:CHANGEPCTDAY data)
|
||||
:change-pct-24h (:CHANGEPCT24HOUR data)
|
||||
:change-24h (:CHANGE24HOUR data)}))
|
||||
{}
|
||||
market-values))
|
||||
|
||||
(defn normalize-token-details
|
||||
[token-details]
|
||||
(reduce-kv (fn [acc token-symbol data]
|
||||
(->> data
|
||||
(cske/transform-keys transforms/->kebab-case-keyword)
|
||||
(assoc acc token-symbol)))
|
||||
{}
|
||||
token-details))
|
||||
|
||||
(defn normalize-prices
|
||||
[token-prices]
|
||||
(reduce-kv
|
||||
(fn [acc k v]
|
||||
(->> v
|
||||
(map (fn [[currency price]]
|
||||
[currency {:price price}]))
|
||||
(into {})
|
||||
(assoc acc k)))
|
||||
{}
|
||||
tokens))
|
||||
|
||||
(defn tokens-by-address
|
||||
[props]
|
||||
(tokens-by-key (assoc props
|
||||
:key-fn
|
||||
(fn [{:keys [chainId address]}]
|
||||
(str chainId "-" address)))))
|
||||
|
||||
(defn tokens-by-symbol
|
||||
[props]
|
||||
(tokens-by-key
|
||||
(assoc props
|
||||
:key-fn
|
||||
(fn [{:keys [chainId address] :as token}]
|
||||
(str chainId "-" (if (get-in token [:communityData :id]) address (:symbol token)))))))
|
||||
token-prices))
|
||||
|
||||
@@ -4,22 +4,15 @@
|
||||
[utils.re-frame :as rf]))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.wallet.tokens/fetch-market-values
|
||||
:effects.wallet.tokens/fetch-additional-token-data
|
||||
(fn [{:keys [symbols currency on-success on-error]}]
|
||||
(-> (rpc/fetch-market-values symbols currency)
|
||||
(-> (rpc/fetch-additional-token-data symbols currency)
|
||||
(promesa/then (partial rf/call-continuation on-success))
|
||||
(promesa/catch (partial rf/call-continuation on-error)))))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.wallet.tokens/fetch-details
|
||||
(fn [{:keys [symbols on-success on-error]}]
|
||||
(-> (rpc/fetch-details symbols)
|
||||
(promesa/then (partial rf/call-continuation on-success))
|
||||
(promesa/catch (partial rf/call-continuation on-error)))))
|
||||
|
||||
(rf/reg-fx
|
||||
:effects.wallet.tokens/fetch-prices
|
||||
(fn [{:keys [symbols currencies on-success on-error]}]
|
||||
(-> (rpc/fetch-prices symbols currencies)
|
||||
:effects.wallet.tokens/fetch-token-list
|
||||
(fn [{:keys [on-success on-error]}]
|
||||
(-> (rpc/fetch-token-list)
|
||||
(promesa/then (partial rf/call-continuation on-success))
|
||||
(promesa/catch (partial rf/call-continuation on-error)))))
|
||||
|
||||
@@ -4,144 +4,51 @@
|
||||
[status-im.contexts.wallet.tokens.data :as tokens-data]
|
||||
[status-im.contexts.wallet.tokens.effects]
|
||||
[taoensso.timbre :as log]
|
||||
[utils.address]
|
||||
[utils.ethereum.chain :as chain]))
|
||||
[utils.address]))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet.tokens/get-token-list
|
||||
;; NOTE: fetching the token list and the rest of the data (market-values, token-details, token-prices)
|
||||
;; separately, since getting the token list is about 10x quicker.
|
||||
|
||||
(rf/reg-event-fx :wallet.tokens/get-token-list
|
||||
(fn [{:keys [db]}]
|
||||
(when-not (get-in db [:wallet :tokens])
|
||||
{:db (assoc-in db [:wallet :ui :loading :token-list] true)
|
||||
:fx [[:json-rpc/call
|
||||
[{:method "wallet_getTokenList"
|
||||
:params []
|
||||
:on-success [:wallet.tokens/store-token-list]
|
||||
:on-error [:wallet.tokens/get-token-list-failed]}]]]})))
|
||||
(when-not (-> db :wallet :tokens)
|
||||
{:fx [[:effects.wallet.tokens/fetch-token-list
|
||||
{:on-success [:wallet.tokens/store-token-list]
|
||||
:on-error [:wallet.tokens/get-token-list-failed]}]]})))
|
||||
|
||||
(defn store-token-list
|
||||
[{:keys [db]} [{:keys [data]}]]
|
||||
(let [chain-ids (chain/chain-ids db)
|
||||
tokens (reduce (fn [{:keys [by-address by-symbol] :as data}
|
||||
{:keys [name source version tokens]}]
|
||||
(-> data
|
||||
(update :sources
|
||||
conj
|
||||
{:name name
|
||||
:source source
|
||||
:version version
|
||||
:tokens-count (count tokens)})
|
||||
(update :by-address
|
||||
merge
|
||||
(tokens-data/tokens-by-address
|
||||
{:added-tokens by-address
|
||||
:source-name name
|
||||
:tokens tokens
|
||||
:chain-ids chain-ids}))
|
||||
(update :by-symbol
|
||||
merge
|
||||
(tokens-data/tokens-by-symbol
|
||||
{:added-tokens by-symbol
|
||||
:source-name name
|
||||
:tokens tokens
|
||||
:chain-ids chain-ids}))))
|
||||
{:sources []
|
||||
:by-address {}
|
||||
:by-symbol {}}
|
||||
data)
|
||||
symbols (->> tokens
|
||||
:by-symbol
|
||||
vals
|
||||
(map :symbol)
|
||||
set
|
||||
vec)]
|
||||
{:fx [[:effects.wallet.tokens/fetch-market-values
|
||||
{:symbols symbols
|
||||
:currency constants/profile-default-currency
|
||||
:on-success [:wallet.tokens/store-market-values]
|
||||
:on-error [:wallet.tokens/fetch-market-values-failed]}]
|
||||
[:effects.wallet.tokens/fetch-details
|
||||
{:symbols symbols
|
||||
:on-success [:wallet.tokens/store-details]
|
||||
:on-error [:wallet.tokens/fetch-details-failed]}]
|
||||
[:effects.wallet.tokens/fetch-prices
|
||||
{:symbols symbols
|
||||
:currencies [constants/profile-default-currency]
|
||||
:on-success [:wallet.tokens/store-prices]
|
||||
:on-error [:wallet.tokens/fetch-prices-failed]}]]
|
||||
:db (-> db
|
||||
(assoc-in [:wallet :tokens]
|
||||
{:sources (:sources tokens)
|
||||
:by-address (-> tokens :by-address vals)
|
||||
:by-symbol (-> tokens :by-symbol vals)})
|
||||
(assoc-in [:wallet :ui :loading]
|
||||
{:token-list false
|
||||
:market-values true
|
||||
:details true
|
||||
:prices true}))}))
|
||||
(rf/reg-event-fx :wallet.tokens/store-token-list
|
||||
(fn [{:keys [db]} [{:keys [sources tokens-by-symbol symbol-lists]}]]
|
||||
(let [all-symbols (tokens-data/all-token-symbols symbol-lists)
|
||||
currency (or (-> db :profile/profile :currency)
|
||||
constants/profile-default-currency)]
|
||||
{:db (assoc-in db
|
||||
[:wallet :tokens]
|
||||
{:sources sources
|
||||
:symbol-lists symbol-lists
|
||||
:tokens-by-symbol tokens-by-symbol})
|
||||
:fx [[:effects.wallet.tokens/fetch-additional-token-data
|
||||
{:currency currency
|
||||
:symbols all-symbols
|
||||
:on-success [:wallet.tokens/store-additional-token-data]
|
||||
:on-error [:wallet.tokens/get-token-list-failed]}]]})))
|
||||
|
||||
(rf/reg-event-fx :wallet.tokens/store-token-list store-token-list)
|
||||
(rf/reg-event-fx :wallet.tokens/store-additional-token-data
|
||||
(fn [{:keys [db]} [{:keys [market-values prices details]}]]
|
||||
{:db (update-in db
|
||||
[:wallet :tokens]
|
||||
assoc
|
||||
:market-values-by-symbol market-values
|
||||
:prices-by-symbol prices
|
||||
:details-by-symbol details)}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet.tokens/get-token-list-failed
|
||||
(fn [{:keys [db]} [error]]
|
||||
(log/info "failed to get wallet tokens "
|
||||
(rf/reg-event-fx :wallet.tokens/get-token-list-failed
|
||||
(fn [_ [error]]
|
||||
(log/info "failed to get wallet tokens"
|
||||
{:error error
|
||||
:event :wallet.tokens/get-token-list})
|
||||
{:db (assoc-in db [:wallet :ui :loading :token-list] false)}))
|
||||
:event :wallet.tokens/get-token-list-failed})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet.tokens/store-market-values
|
||||
(fn [{:keys [db]} [raw-data]]
|
||||
(let [market-values (reduce (fn [acc [token data]]
|
||||
(assoc acc
|
||||
token
|
||||
{:market-cap (:MKTCAP data)
|
||||
:high-day (:HIGHDAY data)
|
||||
:low-day (:LOWDAY data)
|
||||
:change-pct-hour (:CHANGEPCTHOUR data)
|
||||
:change-pct-day (:CHANGEPCTDAY data)
|
||||
:change-pct-24h (:CHANGEPCT24HOUR data)
|
||||
:change-24h (:CHANGE24HOUR data)}))
|
||||
{}
|
||||
raw-data)]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :tokens :market-values-per-token] market-values)
|
||||
(assoc-in [:wallet :ui :loading :market-values] false))})))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet.tokens/fetch-market-values-failed
|
||||
(fn [{:keys [db]} [error]]
|
||||
(log/info "failed to get wallet market values "
|
||||
(rf/reg-event-fx :wallet.tokens/get-additional-token-data-failed
|
||||
(fn [_ [error]]
|
||||
(log/info "failed to get wallet token data"
|
||||
{:error error
|
||||
:event :wallet.tokens/fetch-market-values})
|
||||
{:db (assoc-in db [:wallet :ui :loading :market-values] false)}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet.tokens/store-details
|
||||
(fn [{:keys [db]} [details]]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :tokens :details-per-token] details)
|
||||
(assoc-in [:wallet :ui :loading :details] false))}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet.tokens/fetch-details-failed
|
||||
(fn [{:keys [db]} [error]]
|
||||
(log/info "failed to get wallet details "
|
||||
{:error error
|
||||
:event :wallet.tokens/fetch-details})
|
||||
{:db (assoc-in db [:wallet :ui :loading :details] false)}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet.tokens/store-prices
|
||||
(fn [{:keys [db]} [prices]]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :tokens :prices-per-token] prices)
|
||||
(assoc-in [:wallet :ui :loading :prices] false))}))
|
||||
|
||||
(rf/reg-event-fx
|
||||
:wallet.tokens/fetch-prices-failed
|
||||
(fn [{:keys [db]} [error]]
|
||||
(log/info "failed to get wallet prices "
|
||||
{:error error
|
||||
:event :wallet.tokens/fetch-prices})
|
||||
{:db (assoc-in db [:wallet :ui :loading :prices] false)}))
|
||||
:event :wallet.tokens/get-additional-token-data-failed})))
|
||||
|
||||
@@ -1,19 +1,39 @@
|
||||
(ns status-im.contexts.wallet.tokens.rpc
|
||||
(:require [promesa.core :as promesa]
|
||||
[status-im.common.json-rpc.events :as rpc-events]
|
||||
[status-im.contexts.wallet.tokens.data :as tokens-data]
|
||||
[utils.transforms :as transforms]))
|
||||
|
||||
(defn fetch-token-list
|
||||
[]
|
||||
(-> (rpc-events/call-async "wallet_getTokenList" true)
|
||||
(promesa/then transforms/js->clj)
|
||||
(promesa/then tokens-data/normalize-tokens)))
|
||||
|
||||
(defn fetch-market-values
|
||||
[symbols currency]
|
||||
(-> (rpc-events/call-async "wallet_fetchMarketValues" true symbols currency)
|
||||
(promesa/then transforms/js->clj)))
|
||||
(promesa/then transforms/js->clj)
|
||||
(promesa/then tokens-data/normalize-market-values)))
|
||||
|
||||
(defn fetch-details
|
||||
[symbols]
|
||||
(-> (rpc-events/call-async "wallet_fetchTokenDetails" true symbols)
|
||||
(promesa/then transforms/js->clj)))
|
||||
(promesa/then transforms/js->clj)
|
||||
(promesa/then tokens-data/normalize-token-details)))
|
||||
|
||||
(defn fetch-prices
|
||||
[symbols currencies]
|
||||
(-> (rpc-events/call-async "wallet_fetchPrices" true symbols currencies)
|
||||
(promesa/then transforms/js->clj)))
|
||||
(promesa/then transforms/js->clj)
|
||||
(promesa/then tokens-data/normalize-prices)))
|
||||
|
||||
(defn fetch-additional-token-data
|
||||
[symbols currency]
|
||||
(-> (promesa/all [(fetch-market-values symbols currency)
|
||||
(fetch-details symbols)
|
||||
(fetch-prices symbols [currency])])
|
||||
(promesa/then (fn [[market-values details prices]]
|
||||
{:market-values market-values
|
||||
:details details
|
||||
:prices prices}))))
|
||||
|
||||
@@ -474,33 +474,65 @@
|
||||
sorted-tokens))
|
||||
sorted-tokens))))
|
||||
|
||||
;; NOTE: ideally we just pass the symbols list and individual components get the details themselves
|
||||
(rf/reg-sub
|
||||
:wallet.swap/popular-token-symbols
|
||||
:<- [:wallet/tokens]
|
||||
(fn [{:keys [symbol-lists tokens-by-symbol]} [_ chain-id query]]
|
||||
(when symbol-lists
|
||||
(let [tokens (->> (get symbol-lists chain-id)
|
||||
(mapv (fn [token-symbol]
|
||||
{:type constants/swap-tokens-popular
|
||||
:name (get-in tokens-by-symbol [chain-id (keyword token-symbol) :name])
|
||||
:symbol token-symbol})))]
|
||||
(if query
|
||||
(let [query-string (string/lower-case query)]
|
||||
(filter #(or (string/starts-with? (string/lower-case (:name %)) query-string)
|
||||
(string/starts-with? (string/lower-case (str (:symbol %))) query-string))
|
||||
tokens))
|
||||
tokens)))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet.swap/get-popular-token-data
|
||||
:<- [:wallet/tokens]
|
||||
(fn [{:keys [tokens-by-symbol market-values-by-symbol prices-by-symbol details-by-symbol]}
|
||||
[_ chain-id token-symbol]]
|
||||
(let [token-symbol (keyword token-symbol)]
|
||||
(-> tokens-by-symbol
|
||||
(get-in [chain-id token-symbol])
|
||||
(assoc :market-values (get market-values-by-symbol token-symbol)
|
||||
:details (get details-by-symbol token-symbol)
|
||||
:market-values-per-currency (get prices-by-symbol token-symbol))))))
|
||||
|
||||
(-> (rf/subscribe [:wallet.swap/popular-token-symbols 1 "ETH"])
|
||||
deref)
|
||||
|
||||
;; NOTE: even with this structure, we don't have to filter out the chain-ids, as they are already
|
||||
;; split by chain-ids
|
||||
(rf/reg-sub
|
||||
:wallet/all-tokens-by-chain-id
|
||||
:<- [:wallet/tokens]
|
||||
(fn [{:keys [symbol-lists tokens-by-symbol market-values-by-symbol prices-by-symbol details-by-symbol]}
|
||||
[_ chain-id]]
|
||||
(->> (get symbol-lists chain-id)
|
||||
(map (fn [token-symbol]
|
||||
(-> tokens-by-symbol
|
||||
(get-in [chain-id (keyword token-symbol)])
|
||||
(assoc :market-values (get market-values-by-symbol token-symbol)
|
||||
:details (get details-by-symbol token-symbol)
|
||||
:market-values-per-currency (get prices-by-symbol token-symbol))))))))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/tokens-filtered
|
||||
:<- [:wallet/tokens]
|
||||
:<- [:profile/currency]
|
||||
(fn [[{:keys [by-symbol market-values-per-token details-per-token prices-per-token]} currency]
|
||||
[_ {:keys [query chain-ids hide-token-fn]}]]
|
||||
(let [tokens (->> by-symbol
|
||||
(map (fn [token]
|
||||
(let [token-symbol (keyword (:symbol token))
|
||||
price (get-in prices-per-token [token-symbol currency])]
|
||||
(-> token
|
||||
(assoc :market-values
|
||||
(get market-values-per-token token-symbol))
|
||||
(assoc :details (get details-per-token token-symbol))
|
||||
(assoc-in [:market-values-per-currency currency :price]
|
||||
price)))))
|
||||
(filter (fn [{:keys [chain-id]}]
|
||||
(some #{chain-id} chain-ids)))
|
||||
(remove #(when hide-token-fn
|
||||
(hide-token-fn constants/swap-tokens-popular %))))
|
||||
sorted-tokens (utils/sort-tokens-by-name tokens)]
|
||||
(if query
|
||||
(let [query-string (string/lower-case query)]
|
||||
(filter #(or (string/starts-with? (string/lower-case (:name %)) query-string)
|
||||
(string/starts-with? (string/lower-case (:symbol %)) query-string))
|
||||
sorted-tokens))
|
||||
sorted-tokens))))
|
||||
(fn [[_ {:keys [chain-ids]}]]
|
||||
(rf/subscribe [:wallet/all-tokens-by-chain-id (first chain-ids)]))
|
||||
(fn [tokens [_ {:keys [query]}]]
|
||||
(if query
|
||||
(let [query-string (string/lower-case query)]
|
||||
(filter #(or (string/starts-with? (string/lower-case (:name %)) query-string)
|
||||
(string/starts-with? (string/lower-case (:symbol %)) query-string))
|
||||
tokens))
|
||||
tokens)))
|
||||
|
||||
(rf/reg-sub
|
||||
:wallet/token-by-symbol
|
||||
|
||||
Reference in New Issue
Block a user