diff --git a/.env b/.env index 6082719493..51a1ec153a 100644 --- a/.env +++ b/.env @@ -15,3 +15,4 @@ EXTENSIONS=1 HARDWALLET_ENABLED=0 PFS_ENCRYPTION_ENABLED=0 DEV_BUILD=1 +ERC20_CONTRACT_WARNINGS=1 diff --git a/.env.e2e b/.env.e2e index f78e900292..92b61b77fd 100644 --- a/.env.e2e +++ b/.env.e2e @@ -10,3 +10,4 @@ DEBUG_WEBVIEW=1 GROUP_CHATS_ENABLED=1 EXTENSIONS=1 PFS_ENCRYPTION_ENABLED=0 +ERC20_CONTRACT_WARNINGS=1 diff --git a/.env.jenkins b/.env.jenkins index 9df85ce87b..584e1fad24 100644 --- a/.env.jenkins +++ b/.env.jenkins @@ -14,3 +14,4 @@ CACHED_WEBVIEWS_ENABLED=1 EXTENSIONS=1 PFS_ENCRYPTION_ENABLED=0 PAIRING_ENABLED=1 +ERC20_CONTRACT_WARNINGS=1 diff --git a/.env.nightly b/.env.nightly index 79137d0f71..edc0696e6e 100644 --- a/.env.nightly +++ b/.env.nightly @@ -13,3 +13,4 @@ PAIRING_ENABLED=1 MAINNET_WARNING_ENABLED=1 EXTENSIONS=1 PFS_ENCRYPTION_ENABLED=0 +ERC20_CONTRACT_WARNINGS=1 diff --git a/.env.nightly.staging.fleet b/.env.nightly.staging.fleet index 4143b2efd9..4d26cbed8b 100644 --- a/.env.nightly.staging.fleet +++ b/.env.nightly.staging.fleet @@ -11,3 +11,4 @@ GROUP_CHATS_ENABLED=0 MAINNET_WARNING_ENABLED=1 EXTENSIONS=1 PFS_ENCRYPTION_ENABLED=0 +ERC20_CONTRACT_WARNINGS=1 diff --git a/.env.prod b/.env.prod index 381f6f63e3..62f6494e08 100644 --- a/.env.prod +++ b/.env.prod @@ -13,3 +13,4 @@ GROUP_CHATS_ENABLED=0 MAINNET_WARNING_ENABLED=1 EXTENSIONS=1 PFS_ENCRYPTION_ENABLED=0 +ERC20_CONTRACT_WARNINGS=0 diff --git a/src/status_im/chat/commands/impl/transactions.cljs b/src/status_im/chat/commands/impl/transactions.cljs index 8688759307..7708b8d6ff 100644 --- a/src/status_im/chat/commands/impl/transactions.cljs +++ b/src/status_im/chat/commands/impl/transactions.cljs @@ -87,12 +87,10 @@ (defn personal-send-request-short-preview [label-key {:keys [content]}] - (let [{:keys [amount asset network]} (:params content) - token (when (and network asset) - (tokens/asset-for (keyword network) (keyword asset)))] + (let [{:keys [amount coin]} (:params content)] [chat-preview/text {} (i18n/label label-key {:amount (i18n/label-number amount) - :asset (wallet.utils/display-symbol token)})])) + :asset (wallet.utils/display-symbol coin)})])) (def personal-send-request-params [{:id :asset @@ -146,15 +144,16 @@ ;;TODO(goranjovic): currently we only allow tokens which are enabled in Manage assets here ;; because balances are only fetched for them. Revisit this decision with regard to battery/network consequences ;; if we were to update all balances. -(defn- allowed-assets [{:account/keys [account] :keys [chain]}] - (let [chain-keyword (keyword chain) +(defn- allowed-assets [{:account/keys [account] :keys [chain] :as db}] + (let [all-tokens (:wallet/all-tokens db) + chain-keyword (keyword chain) {:keys [symbol symbol-display decimals]} (tokens/native-currency chain-keyword) visible-tokens (get-in account [:settings :wallet :visible-tokens chain-keyword])] (into {(name (or symbol-display symbol)) decimals} (comp (filter #(and (not (:nft? %)) (contains? visible-tokens (:symbol %)))) (map (juxt (comp name :symbol) :decimals))) - (tokens/tokens-for chain-keyword)))) + (tokens/tokens-for all-tokens chain-keyword)))) (defn- personal-send-request-validation [{:keys [asset amount]} {:keys [db]}] (let [asset-decimals (get (allowed-assets db) asset)] @@ -210,11 +209,12 @@ (defview send-preview [{:keys [content timestamp-str outgoing group-chat]}] - (letsubs [network [:network-name]] + (letsubs [network [:network-name] + all-tokens [:wallet/all-tokens]] (let [{{:keys [amount fiat-amount tx-hash asset currency] send-network :network} :params} content recipient-name (get-in content [:params :bot-db :public :recipient]) network-mismatch? (and (seq send-network) (not= network send-network)) - token (tokens/asset-for (keyword send-network) (keyword asset))] + token (tokens/asset-for all-tokens (keyword send-network) (keyword asset))] [react/view transactions-styles/command-send-message-view [react/view [react/view transactions-styles/command-send-amount-row @@ -258,6 +258,12 @@ (defn- inject-network-info [parameters {:keys [db]}] (assoc parameters :network (:chain db))) +(defn- inject-coin-info [{:keys [network asset] :as parameters} {:keys [db]}] + (let [all-tokens (:wallet/all-tokens db) + coin (when (and network asset) + (tokens/asset-for all-tokens (keyword network) (keyword asset)))] + (assoc parameters :coin coin))) + (defn- inject-price-info [{:keys [amount asset] :as parameters} {:keys [db]}] (let [currency (-> db currency-settings.subs/get-currency @@ -305,7 +311,8 @@ sender-account (:account/account db) chain (keyword (:chain db)) symbol-param (keyword asset) - {:keys [symbol decimals]} (tokens/asset-for chain symbol-param) + all-tokens (:wallet/all-tokens db) + {:keys [symbol decimals]} (tokens/asset-for all-tokens chain symbol-param) {:keys [value error]} (wallet.db/parse-amount amount decimals) next-view-id (if (:wallet-set-up-passed? sender-account) :wallet-send-transaction-modal @@ -330,10 +337,14 @@ (navigation/navigate-to-cofx next-view-id {})))) protocol/EnhancedParameters (enhance-send-parameters [_ parameters cofx] - (-> (inject-network-info parameters cofx) + (-> parameters + (inject-network-info cofx) + (inject-coin-info cofx) (inject-price-info cofx))) (enhance-receive-parameters [_ parameters cofx] - (inject-price-info parameters cofx))) + (-> parameters + (inject-coin-info cofx) + (inject-price-info cofx)))) ;; `/request` command @@ -465,7 +476,11 @@ (request-preview command-message)) protocol/EnhancedParameters (enhance-send-parameters [_ parameters cofx] - (-> (inject-network-info parameters cofx) + (-> parameters + (inject-network-info cofx) + (inject-coin-info cofx) (inject-price-info cofx))) (enhance-receive-parameters [_ parameters cofx] - (inject-price-info parameters cofx))) + (-> parameters + (inject-coin-info cofx) + (inject-price-info cofx)))) diff --git a/src/status_im/data_store/realm/schemas/base/core.cljs b/src/status_im/data_store/realm/schemas/base/core.cljs index 70ca1620a3..e11a8863a5 100644 --- a/src/status_im/data_store/realm/schemas/base/core.cljs +++ b/src/status_im/data_store/realm/schemas/base/core.cljs @@ -64,6 +64,8 @@ extension/v12 account/v15]) +(def v17 v16) + ;; put schemas ordered by version (def schemas [{:schema v1 :schemaVersion 1 @@ -112,4 +114,7 @@ :migration migrations/v15} {:schema v16 :schemaVersion 16 - :migration migrations/v16}]) + :migration migrations/v16} + {:schema v17 + :schemaVersion 17 + :migration migrations/v17}]) diff --git a/src/status_im/data_store/realm/schemas/base/migrations.cljs b/src/status_im/data_store/realm/schemas/base/migrations.cljs index e2b30d9da4..b9775cad07 100644 --- a/src/status_im/data_store/realm/schemas/base/migrations.cljs +++ b/src/status_im/data_store/realm/schemas/base/migrations.cljs @@ -114,3 +114,16 @@ (defn v16 [old-realm new-realm] (log/debug "migrating base database v16: " old-realm new-realm)) + +(defn v17 [old-realm new-realm] + (log/debug "migrating accounts schema v17") + (let [accounts (.objects new-realm "account")] + (dotimes [i (.-length accounts)] + (let [account (aget accounts i) + old-settings (deserialize (aget account "settings")) + new-settings (update-in old-settings [:wallet :visible-tokens :mainnet] + #(cond-> % + true (disj :BQX) + (contains? % :BQX) (conj :ETHOS))) + updated (serialize new-settings)] + (aset account "settings" updated))))) diff --git a/src/status_im/init/core.cljs b/src/status_im/init/core.cljs index 3266cbedaa..fe9a60b27d 100644 --- a/src/status_im/init/core.cljs +++ b/src/status_im/init/core.cljs @@ -180,6 +180,7 @@ (defn initialize-wallet [cofx] (fx/merge cofx + (models.wallet/initialize-tokens) (models.wallet/update-wallet) (transactions/start-sync))) diff --git a/src/status_im/models/transactions.cljs b/src/status_im/models/transactions.cljs index e83d848d03..e01196eb07 100644 --- a/src/status_im/models/transactions.cljs +++ b/src/status_im/models/transactions.cljs @@ -360,11 +360,11 @@ (defonce polling-executor (atom nil)) -(defn transactions-query-helper [web3 account-address chain done-fn] +(defn transactions-query-helper [web3 all-tokens account-address chain done-fn] (get-transactions {:account-address account-address :chain chain - :chain-tokens (into {} (map (juxt :address identity) (tokens/tokens-for chain))) + :chain-tokens (into {} (map (juxt :address identity) (tokens/tokens-for all-tokens chain))) :web3 web3 :success-fn (fn [transactions] #_(log/debug "Transactions received: " (pr-str (keys transactions))) @@ -380,7 +380,7 @@ (log/debug "Unable to get transactions: " http-error) (done-fn))})) -(defn- sync-now! [{:keys [network-status :account/account app-state network web3] :as opts}] +(defn- sync-now! [{:keys [network-status :account/account :wallet/all-tokens app-state network web3] :as opts}] (when @polling-executor (let [chain (ethereum/network->chain-keyword (get-in account [:networks network])) account-address (:address account)] @@ -389,11 +389,11 @@ (not= :custom chain)) (async-periodic-run! @polling-executor - (partial transactions-query-helper web3 account-address chain)))))) + (partial transactions-query-helper web3 all-tokens account-address chain)))))) ;; this function handles background syncing of transactions (defn- background-sync [web3 account-address done-fn] - (let [{:keys [network network-status :account/account app-state wallet chats]} @re-frame.db/app-db + (let [{:keys [network network-status :account/account app-state wallet chats :wallet/all-tokens]} @re-frame.db/app-db chain (ethereum/network->chain-keyword (get-in account [:networks network]))] (assert (and web3 account-address network network-status account app-state wallet chats) "Must have all necessary data to run background transaction sync") @@ -407,7 +407,7 @@ (if-not (or (have-unconfirmed-transactions? (vals transaction-map)) (not-empty (set/difference chat-transaction-ids transaction-ids))) (done-fn) - (transactions-query-helper web3 account-address chain done-fn)))))) + (transactions-query-helper web3 all-tokens account-address chain done-fn)))))) (defn- start-sync! [{:keys [:account/account network web3] :as options}] (let [account-address (:address account)] diff --git a/src/status_im/models/wallet.cljs b/src/status_im/models/wallet.cljs index f6b8712dd3..be30c01c69 100644 --- a/src/status_im/models/wallet.cljs +++ b/src/status_im/models/wallet.cljs @@ -7,9 +7,11 @@ [status-im.utils.ethereum.core :as ethereum] [status-im.utils.ethereum.tokens :as tokens] [status-im.utils.hex :as utils.hex] + [status-im.utils.core :as utils.core] [status-im.utils.money :as money] [status-im.utils.fx :as fx] - [status-im.ui.screens.wallet.utils :as wallet.utils])) + [status-im.ui.screens.wallet.utils :as wallet.utils] + [status-im.utils.config :as config])) (def min-gas-price-wei (money/bignumber 1)) @@ -144,9 +146,10 @@ {:dispatch (conj on-error "transaction was cancelled by user")})))) (defn prepare-unconfirmed-transaction [db now hash] - (let [transaction (get-in db [:wallet :send-transaction])] + (let [transaction (get-in db [:wallet :send-transaction]) + all-tokens (:wallet/all-tokens db)] (let [chain (:chain db) - token (tokens/symbol->token (keyword chain) (:symbol transaction))] + token (tokens/symbol->token all-tokens (keyword chain) (:symbol transaction))] (-> transaction (assoc :confirmations "0" :timestamp (str now) @@ -183,16 +186,29 @@ (defn clear-error-message [db error-type] (update-in db [:wallet :errors] dissoc error-type)) -(defn tokens-symbols [v chain] - (set/difference (set v) (set (map :symbol (tokens/nfts-for chain))))) +(defn tokens-symbols [visible-token-symbols all-tokens chain] + (set/difference (set visible-token-symbols) (set (map :symbol (tokens/nfts-for all-tokens chain))))) + +(fx/defn initialize-tokens + [{:keys [db]}] + (let [network-id (get-in db [:account/account :network]) + network (get-in db [:account/account :networks network-id]) + chain (ethereum/network->chain-keyword network)] + (merge + {:db (assoc db :wallet/all-tokens + (utils.core/map-values #(utils.core/index-by :address %) tokens/all-default-tokens))} + (when config/erc20-contract-warnings-enabled? + {:wallet/validate-tokens {:web3 (:web3 db) + :tokens (get tokens/all-default-tokens chain)}})))) (fx/defn update-wallet [{{:keys [web3 network network-status] {:keys [address settings]} :account/account :as db} :db}] - (let [network (get-in db [:account/account :networks network]) + (let [all-tokens (:wallet/all-tokens db) + network (get-in db [:account/account :networks network]) chain (ethereum/network->chain-keyword network) mainnet? (= :mainnet chain) assets (get-in settings [:wallet :visible-tokens chain]) - tokens (tokens-symbols (get-in settings [:wallet :visible-tokens chain]) chain) + tokens (tokens-symbols (get-in settings [:wallet :visible-tokens chain]) all-tokens chain) currency-id (or (get-in settings [:wallet :currency]) :usd) currency (get constants/currencies currency-id)] (when (not= network-status :offline) @@ -204,6 +220,7 @@ :account-id address :symbols assets :chain chain + :all-tokens all-tokens :success-event :update-token-balance-success :error-event :update-token-balance-fail} :get-prices {:from (if mainnet? diff --git a/src/status_im/ui/screens/db.cljs b/src/status_im/ui/screens/db.cljs index 202f4090d6..cf97ea3380 100644 --- a/src/status_im/ui/screens/db.cljs +++ b/src/status_im/ui/screens/db.cljs @@ -37,6 +37,7 @@ :app-state "active" :wallet.transactions constants/default-wallet-transactions :wallet-selected-asset {} + :wallet/all-tokens {} :prices {} :peers-count 0 :peers-summary [] @@ -243,6 +244,7 @@ :desktop/desktop :dimensions/window :dapps/permissions + :wallet/all-tokens :ui/contact :ui/search :ui/chat] diff --git a/src/status_im/ui/screens/wallet/choose_recipient/events.cljs b/src/status_im/ui/screens/wallet/choose_recipient/events.cljs index 70b5275897..d90a7d72d3 100644 --- a/src/status_im/ui/screens/wallet/choose_recipient/events.cljs +++ b/src/status_im/ui/screens/wallet/choose_recipient/events.cljs @@ -39,9 +39,9 @@ (defn- extract-details "First try to parse as EIP681 URI, if not assume this is an address directly. Returns a map containing at least the `address` and `chain-id` keys" - [s chain-id] + [s chain-id all-tokens] (or (let [m (eip681/parse-uri s)] - (merge m (eip681/extract-request-details m))) + (merge m (eip681/extract-request-details m all-tokens))) (when (ethereum/address? s) {:address s :chain-id chain-id}))) @@ -87,9 +87,9 @@ (handlers/register-handler-fx :wallet/fill-request-from-url - (fn [{{:keys [network] :as db} :db} [_ data origin]] + (fn [{{:keys [network] :wallet/keys [all-tokens] :as db} :db} [_ data origin]] (let [current-chain-id (get-in constants/default-networks [network :config :NetworkId]) - {:keys [address chain-id] :as details} (extract-details data current-chain-id) + {:keys [address chain-id] :as details} (extract-details data current-chain-id all-tokens) valid-network? (boolean (= current-chain-id chain-id)) previous-state (get-in db [:wallet :send-transaction]) old-symbol (:symbol previous-state) diff --git a/src/status_im/ui/screens/wallet/collectibles/cryptokitties/events.cljs b/src/status_im/ui/screens/wallet/collectibles/cryptokitties/events.cljs index 0f62201147..dc02e57fe3 100644 --- a/src/status_im/ui/screens/wallet/collectibles/cryptokitties/events.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/cryptokitties/events.cljs @@ -18,7 +18,7 @@ ids)})) ;; TODO(andrey) Each HTTP call will return up to 100 kitties. Maybe we need to implement some kind of paging later -(defmethod collectibles/load-collectibles-fx ck [_ _ items-number address _] +(defmethod collectibles/load-collectibles-fx ck [_ _ _ items-number address _] {:http-get {:url (str "https://api.cryptokitties.co/kitties?offset=0&limit=" items-number "&owner_wallet_address=" diff --git a/src/status_im/ui/screens/wallet/collectibles/events.cljs b/src/status_im/ui/screens/wallet/collectibles/events.cljs index 912b1b8ec2..b352a1eda4 100644 --- a/src/status_im/ui/screens/wallet/collectibles/events.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/events.cljs @@ -12,19 +12,20 @@ (defmethod load-collectible-fx :default [_ _ _] nil) -(defmulti load-collectibles-fx (fn [_ symbol _ _] symbol)) +(defmulti load-collectibles-fx (fn [_ _ symbol _ _] symbol)) -(defmethod load-collectibles-fx :default [web3 symbol items-number address chain-id] - {:load-collectibles-fx [web3 symbol items-number address chain-id]}) +(defmethod load-collectibles-fx :default [web3 all-tokens symbol items-number address chain-id] + {:load-collectibles-fx [web3 all-tokens symbol items-number address chain-id]}) (handlers/register-handler-fx :show-collectibles-list (fn [{:keys [db]} [_ address {:keys [symbol amount] :as collectible}]] - (let [chain-id (get-in constants/default-networks [(:network db) :config :NetworkId]) + (let [chain-id (get-in constants/default-networks [(:network db) :config :NetworkId]) + all-tokens (:wallet/all-tokens db) items-number (money/to-number amount) loaded-items-number (count (get-in db [:collectibles symbol]))] (merge (when (not= items-number loaded-items-number) - (load-collectibles-fx (:web3 db) symbol items-number address chain-id)) + (load-collectibles-fx (:web3 db) all-tokens symbol items-number address chain-id)) {:dispatch [:navigate-to :collectibles-list collectible]})))) (defn load-token [web3 i items-number contract address symbol] @@ -36,9 +37,9 @@ (re-frame/reg-fx :load-collectibles-fx - (fn [[web3 symbol items-number address chain-id]] + (fn [[web3 all-tokens symbol items-number address chain-id]] (let [chain (ethereum/chain-id->chain-keyword chain-id) - contract (:address (tokens/symbol->token chain symbol))] + contract (:address (tokens/symbol->token all-tokens chain symbol))] (load-token web3 0 items-number contract address symbol)))) (handlers/register-handler-fx diff --git a/src/status_im/ui/screens/wallet/collectibles/kudos/events.cljs b/src/status_im/ui/screens/wallet/collectibles/kudos/events.cljs index 1be47ceb77..afdb720379 100644 --- a/src/status_im/ui/screens/wallet/collectibles/kudos/events.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/kudos/events.cljs @@ -12,14 +12,15 @@ (def kudos :KDO) (defmethod collectibles/load-collectible-fx kudos [{db :db} symbol id] - (let [chain-id (get-in constants/default-networks [(:network db) :config :NetworkId])] - {:erc721-token-uri [(:web3 db) symbol id chain-id]})) + (let [chain-id (get-in constants/default-networks [(:network db) :config :NetworkId]) + all-tokens (:wallet/all-tokens db)] + {:erc721-token-uri [(:web3 db) all-tokens symbol id chain-id]})) (re-frame/reg-fx :erc721-token-uri - (fn [[web3 symbol tokenId chain-id]] + (fn [[web3 all-tokens symbol tokenId chain-id]] (let [chain (ethereum/chain-id->chain-keyword chain-id) - contract (:address (tokens/symbol->token chain symbol))] + contract (:address (tokens/symbol->token all-tokens chain symbol))] (erc721/token-uri web3 contract tokenId #(re-frame/dispatch [:token-uri-success tokenId diff --git a/src/status_im/ui/screens/wallet/collectibles/superrare/events.cljs b/src/status_im/ui/screens/wallet/collectibles/superrare/events.cljs index d6c9e40f74..77a86ccac3 100644 --- a/src/status_im/ui/screens/wallet/collectibles/superrare/events.cljs +++ b/src/status_im/ui/screens/wallet/collectibles/superrare/events.cljs @@ -29,7 +29,7 @@ imageUri }}}}")) -(defmethod collectibles/load-collectibles-fx superrare [_ _ _ address _] +(defmethod collectibles/load-collectibles-fx superrare [_ _ _ _ address _] {:http-post {:url graphql-url :data (types/clj->json {:query (graphql-query (ethereum/naked-address address))}) :opts {:headers {"Content-Type" "application/json"}} diff --git a/src/status_im/ui/screens/wallet/components/views.cljs b/src/status_im/ui/screens/wallet/components/views.cljs index 14faa56ae0..e4ff82509e 100644 --- a/src/status_im/ui/screens/wallet/components/views.cljs +++ b/src/status_im/ui/screens/wallet/components/views.cljs @@ -153,9 +153,10 @@ (throw (str "Unknown type: " k)))) (views/defview asset-selector [{:keys [disabled? type symbol error]}] - (views/letsubs [balance [:balance] - network [:network]] - (let [{:keys [name icon decimals] :as token} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)] + (views/letsubs [balance [:balance] + network [:network] + all-tokens [:wallet/all-tokens]] + (let [{:keys [name icon decimals] :as token} (tokens/asset-for all-tokens (ethereum/network->chain-keyword network) symbol)] (when name [react/view [cartouche {:disabled? disabled? :on-press #(re-frame/dispatch [:navigate-to (type->view type)])} diff --git a/src/status_im/ui/screens/wallet/events.cljs b/src/status_im/ui/screens/wallet/events.cljs index 16a025ffb7..05de378013 100644 --- a/src/status_im/ui/screens/wallet/events.cljs +++ b/src/status_im/ui/screens/wallet/events.cljs @@ -11,7 +11,9 @@ [status-im.utils.money :as money] [status-im.utils.prices :as prices] [taoensso.timbre :as log] - [status-im.utils.fx :as fx])) + [status-im.utils.fx :as fx] + [status-im.i18n :as i18n] + [status-im.utils.utils :as utils.utils])) (defn get-balance [{:keys [web3 account-id on-success on-error]}] (if (and web3 account-id) @@ -51,9 +53,9 @@ (re-frame/reg-fx :get-tokens-balance - (fn [{:keys [web3 symbols chain account-id success-event error-event]}] + (fn [{:keys [web3 symbols all-tokens chain account-id success-event error-event]}] (doseq [symbol symbols] - (let [contract (:address (tokens/symbol->token chain symbol))] + (let [contract (:address (tokens/symbol->token all-tokens chain symbol))] (get-token-balance {:web3 web3 :contract contract :account-id account-id @@ -80,6 +82,51 @@ (fn [{:keys [web3 obj success-event]}] (ethereum/estimate-gas-web3 web3 (clj->js obj) #(re-frame/dispatch [success-event %2])))) +(defn- validate-token-name! [web3 {:keys [address symbol name]}] + (erc20/name web3 address #(when (and (seq %2) ;;NOTE(goranjovic): skipping check if field not set in contract + (not= name %2)) + (let [message (i18n/label :t/token-auto-validate-name-error + {:symbol symbol + :expected name + :actual %2 + :address address})] + (log/warn message) + (utils.utils/show-popup (i18n/label :t/warning) message))))) + +(defn- validate-token-symbol! [web3 {:keys [address symbol]}] + (erc20/symbol web3 address #(when (and (seq %2) ;;NOTE(goranjovic): skipping check if field not set in contract + (not= (clojure.core/name symbol) %2)) + (let [message (i18n/label :t/token-auto-validate-symbol-error + {:symbol symbol + :expected (clojure.core/name symbol) + :actual %2 + :address address})] + (log/warn message) + (utils.utils/show-popup (i18n/label :t/warning) message))))) + +(defn- validate-token-decimals! [web3 {:keys [address symbol decimals nft? skip-decimals-check?]}] + ;;NOTE(goranjovic): only skipping check if skip-decimals-check? flag is present because we can't differentiate + ;;between unset decimals and 0 decimals. + (when-not skip-decimals-check? + (erc20/decimals web3 address #(when (and %2 + (not nft?) + (not= decimals (int %2))) + (let [message (i18n/label :t/token-auto-validate-decimals-error + {:symbol symbol + :expected decimals + :actual %2 + :address address})] + (log/warn message) + (utils.utils/show-popup (i18n/label :t/warning) message)))))) + +(re-frame/reg-fx + :wallet/validate-tokens + (fn [{:keys [web3 tokens]}] + (doseq [token tokens] + (validate-token-decimals! web3 token) + (validate-token-symbol! web3 token) + (validate-token-name! web3 token)))) + ;; Handlers (handlers/register-handler-fx :update-wallet diff --git a/src/status_im/ui/screens/wallet/request/views.cljs b/src/status_im/ui/screens/wallet/request/views.cljs index 26535f0424..3a1e5c727d 100644 --- a/src/status_im/ui/screens/wallet/request/views.cljs +++ b/src/status_im/ui/screens/wallet/request/views.cljs @@ -30,9 +30,10 @@ (views/letsubs [network [:account/network] {:keys [to to-name public-key]} [:wallet.send/transaction] {:keys [amount amount-error amount-text symbol]} [:wallet.request/transaction] - network-status [:network-status] - scroll (atom nil)] - (let [{:keys [decimals] :as token} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)] + network-status [:network-status] + all-tokens [:wallet/all-tokens] + scroll (atom nil)] + (let [{:keys [decimals] :as token} (tokens/asset-for all-tokens (ethereum/network->chain-keyword network) symbol)] [wallet.components/simple-screen {:avoid-keyboard? true} [wallet.components/toolbar (i18n/label :t/new-request)] [react/view components.styles/flex diff --git a/src/status_im/ui/screens/wallet/send/events.cljs b/src/status_im/ui/screens/wallet/send/events.cljs index c5b6d2cc64..1e51b35812 100644 --- a/src/status_im/ui/screens/wallet/send/events.cljs +++ b/src/status_im/ui/screens/wallet/send/events.cljs @@ -27,16 +27,16 @@ (security/safe-unmask-data masked-password) on-completed)) -(defn- send-tokens [symbol chain {:keys [from to value gas gasPrice]} on-completed masked-password] - (let [contract (:address (tokens/symbol->token (keyword chain) symbol))] +(defn- send-tokens [all-tokens symbol chain {:keys [from to value gas gasPrice]} on-completed masked-password] + (let [contract (:address (tokens/symbol->token all-tokens (keyword chain) symbol))] (erc20/transfer contract from to value gas gasPrice masked-password on-completed))) (re-frame/reg-fx ::send-transaction - (fn [[params symbol chain on-completed masked-password]] + (fn [[params all-tokens symbol chain on-completed masked-password]] (case symbol :ETH (send-ethers params on-completed masked-password) - (send-tokens symbol chain params on-completed masked-password)))) + (send-tokens all-tokens symbol chain params on-completed masked-password)))) (re-frame/reg-fx ::sign-message @@ -57,12 +57,14 @@ :wallet/send-transaction (fn [{{:keys [chain] :as db} :db} _] (let [{:keys [password symbol in-progress?] :as transaction} (get-in db [:wallet :send-transaction]) - from (get-in db [:account/account :address])] + all-tokens (:wallet/all-tokens db) + from (get-in db [:account/account :address])] (when-not in-progress? {:db (-> db (assoc-in [:wallet :send-transaction :wrong-password?] false) (assoc-in [:wallet :send-transaction :in-progress?] true)) ::send-transaction [(models.wallet/prepare-send-transaction from transaction) + all-tokens symbol chain #(re-frame/dispatch [::transaction-completed (types/json->clj %)]) diff --git a/src/status_im/ui/screens/wallet/send/views.cljs b/src/status_im/ui/screens/wallet/send/views.cljs index d071e54517..95f247cbc3 100644 --- a/src/status_im/ui/screens/wallet/send/views.cljs +++ b/src/status_im/ui/screens/wallet/send/views.cljs @@ -138,12 +138,12 @@ (i18n/label :t/transactions-sign-transaction) [vector-icons/icon :icons/forward {:color (if sign-enabled? colors/white colors/white-light-transparent)}]]])) -(defn- render-send-transaction-view [{:keys [modal? transaction scroll advanced? network amount-input network-status]}] +(defn- render-send-transaction-view [{:keys [modal? transaction scroll advanced? network all-tokens amount-input network-status]}] (let [{:keys [amount amount-text amount-error asset-error show-password-input? to to-name sufficient-funds? sufficient-gas? in-progress? from-chat? symbol]} transaction chain (ethereum/network->chain-keyword network) native-currency (tokens/native-currency chain) - {:keys [decimals] :as token} (tokens/asset-for chain symbol) + {:keys [decimals] :as token} (tokens/asset-for all-tokens chain symbol) online? (= :online network-status)] [wallet.components/simple-screen {:avoid-keyboard? (not modal?) :status-bar-type (if modal? :modal-wallet :wallet)} @@ -202,31 +202,35 @@ ;; SEND TRANSACTION FROM WALLET (CHAT) (defview send-transaction [] - (letsubs [transaction [:wallet.send/transaction] - advanced? [:wallet.send/advanced?] - network [:account/network] - scroll (atom nil) - network-status [:network-status]] - [send-transaction-view {:modal? false - :transaction transaction - :scroll scroll - :advanced? advanced? - :network network + (letsubs [transaction [:wallet.send/transaction] + advanced? [:wallet.send/advanced?] + network [:account/network] + scroll (atom nil) + network-status [:network-status] + all-tokens [:wallet/all-tokens]] + [send-transaction-view {:modal? false + :transaction transaction + :scroll scroll + :advanced? advanced? + :network network + :all-tokens all-tokens :network-status network-status}])) ;; SEND TRANSACTION FROM DAPP (defview send-transaction-modal [] - (letsubs [transaction [:wallet.send/transaction] - advanced? [:wallet.send/advanced?] - network [:account/network] - scroll (atom nil) - network-status [:network-status]] + (letsubs [transaction [:wallet.send/transaction] + advanced? [:wallet.send/advanced?] + network [:account/network] + scroll (atom nil) + network-status [:network-status] + all-tokens [:wallet/all-tokens]] (if transaction - [send-transaction-view {:modal? true - :transaction transaction - :scroll scroll - :advanced? advanced? - :network network + [send-transaction-view {:modal? true + :transaction transaction + :scroll scroll + :advanced? advanced? + :network network + :all-tokens all-tokens :network-status network-status}] [react/view wallet.styles/wallet-modal-container [react/view components.styles/flex diff --git a/src/status_im/ui/screens/wallet/settings/models.cljs b/src/status_im/ui/screens/wallet/settings/models.cljs index e9335c6109..7274312f52 100644 --- a/src/status_im/ui/screens/wallet/settings/models.cljs +++ b/src/status_im/ui/screens/wallet/settings/models.cljs @@ -25,10 +25,10 @@ (wallet.events/update-token-balance-success symbol balance))) (fx/defn wallet-autoconfig-tokens [{:keys [db]}] - (let [{:keys [account/account web3 network-status]} db + (let [{:keys [account/account web3 network-status] :wallet/keys [all-tokens]} db network (get (:networks account) (:network account)) chain (ethereum/network->chain-keyword network) - contracts (->> (tokens/tokens-for chain) + contracts (->> (tokens/tokens-for all-tokens chain) (remove :hidden?))] (when-not (= network-status :offline) (doseq [{:keys [address symbol]} contracts] diff --git a/src/status_im/ui/screens/wallet/settings/views.cljs b/src/status_im/ui/screens/wallet/settings/views.cljs index 265fdab932..ab2c2f07db 100644 --- a/src/status_im/ui/screens/wallet/settings/views.cljs +++ b/src/status_im/ui/screens/wallet/settings/views.cljs @@ -23,7 +23,8 @@ (defview manage-assets [] (letsubs [network [:network] - visible-tokens [:wallet/visible-tokens-symbols]] + visible-tokens [:wallet/visible-tokens-symbols] + all-tokens [:wallet/all-tokens]] [react/view (merge components.styles/flex {:background-color :white}) [status-bar/status-bar {:type :modal-wallet}] [toolbar/toolbar {:style wallet.styles/toolbar} @@ -35,6 +36,6 @@ [toolbar/content-title {:color :white} (i18n/label :t/wallet-assets)]] [react/view {:style components.styles/flex} - [list/flat-list {:data (tokens/sorted-tokens-for (ethereum/network->chain-keyword network)) + [list/flat-list {:data (tokens/sorted-tokens-for all-tokens (ethereum/network->chain-keyword network)) :key-fn (comp str :symbol) :render-fn #(render-token % visible-tokens)}]]])) diff --git a/src/status_im/ui/screens/wallet/subs.cljs b/src/status_im/ui/screens/wallet/subs.cljs index 262f4ebff8..2f8840bc4b 100644 --- a/src/status_im/ui/screens/wallet/subs.cljs +++ b/src/status_im/ui/screens/wallet/subs.cljs @@ -50,14 +50,18 @@ .toNumber)) acc)) 0 balance)) +(re-frame/reg-sub :wallet/all-tokens + (fn [db] (:wallet/all-tokens db))) + (re-frame/reg-sub :portfolio-value :<- [:balance] :<- [:prices] :<- [:wallet/currency] :<- [:network] - (fn [[balance prices currency network] [_ currency-code]] + :<- [:wallet/all-tokens] + (fn [[balance prices currency network all-tokens] [_ currency-code]] (if (and balance prices) - (let [assets (tokens/tokens-for (ethereum/network->chain-keyword network)) + (let [assets (tokens/tokens-for all-tokens (ethereum/network->chain-keyword network)) token->decimals (into {} (map #(vector (:symbol %) (:decimals %)) assets)) balance-total-value (get-balance-total-value balance @@ -102,10 +106,11 @@ (re-frame/reg-sub :wallet/visible-assets :<- [:network] :<- [:wallet/visible-tokens-symbols] - (fn [[network visible-tokens-symbols]] + :<- [:wallet/all-tokens] + (fn [[network visible-tokens-symbols all-tokens]] (let [chain (ethereum/network->chain-keyword network)] (conj (filter #(contains? visible-tokens-symbols (:symbol %)) - (tokens/sorted-tokens-for (ethereum/network->chain-keyword network))) + (tokens/sorted-tokens-for all-tokens (ethereum/network->chain-keyword network))) (tokens/native-currency chain))))) (re-frame/reg-sub :wallet/visible-assets-with-amount diff --git a/src/status_im/ui/screens/wallet/transaction_fee/views.cljs b/src/status_im/ui/screens/wallet/transaction_fee/views.cljs index 37cdabf141..77917b429a 100644 --- a/src/status_im/ui/screens/wallet/transaction_fee/views.cljs +++ b/src/status_im/ui/screens/wallet/transaction_fee/views.cljs @@ -27,13 +27,14 @@ network [:account/network] {gas-edit :gas max-fee :max-fee - gas-price-edit :gas-price} [:wallet/edit]] + gas-price-edit :gas-price} [:wallet/edit] + all-tokens [:wallet/all-tokens]] (let [{:keys [amount symbol]} send-transaction gas (:value gas-edit) gas-price (:value gas-price-edit) chain (ethereum/network->chain-keyword network) native-currency (tokens/native-currency chain) - {:keys [decimals] :as token} (tokens/asset-for chain symbol)] + {:keys [decimals] :as token} (tokens/asset-for all-tokens chain symbol)] [components/simple-screen {:status-bar-type :modal-wallet} [toolbar (i18n/label :t/wallet-transaction-fee)] [react/view components.styles/flex diff --git a/src/status_im/ui/screens/wallet/transactions/views.cljs b/src/status_im/ui/screens/wallet/transactions/views.cljs index 2fec621fbd..7ae5fa8c05 100644 --- a/src/status_im/ui/screens/wallet/transactions/views.cljs +++ b/src/status_im/ui/screens/wallet/transactions/views.cljs @@ -50,13 +50,14 @@ (:postponed :pending) (transaction-icon :icons/arrow-right colors/gray-light colors/gray) (throw (str "Unknown transaction type: " k)))) -(defn render-transaction [{:keys [hash from-contact to-contact to from type value time-formatted symbol]} network hide-details?] +(defn render-transaction [{:keys [hash from-contact to-contact to from type value time-formatted symbol]} + network all-tokens hide-details?] (let [[label contact address contact-accessibility-label address-accessibility-label] (if (inbound? type) [(i18n/label :t/from) from-contact from :sender-text :sender-address-text] [(i18n/label :t/to) to-contact to :recipient-name-text :recipient-address-text]) - {:keys [decimals] :as token} (tokens/asset-for (ethereum/network->chain-keyword network) symbol)] + {:keys [decimals] :as token} (tokens/asset-for all-tokens (ethereum/network->chain-keyword network) symbol)] [list/touchable-item #(when-not hide-details? (re-frame/dispatch [:show-transaction-details hash])) [react/view {:accessibility-label :transaction-item} [list/item @@ -101,11 +102,12 @@ (defview history-list [& [hide-details?]] (letsubs [transactions-history-list [:wallet.transactions/transactions-history-list] filter-data [:wallet.transactions/filters] - network [:account/network]] + network [:account/network] + all-tokens [:wallet/all-tokens]] [react/view components.styles/flex [list/section-list {:sections (map #(update-transactions % filter-data) transactions-history-list) :key-fn :hash - :render-fn #(render-transaction % network hide-details?) + :render-fn #(render-transaction % network all-tokens hide-details?) :empty-component [react/i18n-text {:style styles/empty-text :key :transactions-history-empty}] :on-refresh #(re-frame/dispatch [:update-transactions]) @@ -163,8 +165,8 @@ (-> amount (money/token->unit (:decimals token)) money/to-fixed str)) "...")) -(defn details-header [network {:keys [value date type symbol token]}] - (let [asset (tokens/asset-for (ethereum/network->chain-keyword network) symbol)] +(defn details-header [network all-tokens {:keys [value date type symbol token]}] + (let [asset (tokens/asset-for all-tokens (ethereum/network->chain-keyword network) symbol)] [react/view {:style styles/details-header} [react/view {:style styles/details-header-icon} [list/item-icon (transaction-type->icon type)]] @@ -245,7 +247,8 @@ (letsubs [{:keys [hash url type] :as transaction} [:wallet.transactions/transaction-details] confirmations [:wallet.transactions.details/confirmations] confirmations-progress [:wallet.transactions.details/confirmations-progress] - network [:account/network]] + network [:account/network] + all-tokens [:wallet/all-tokens]] [react/view {:style components.styles/flex} [status-bar/status-bar] [toolbar/toolbar {} @@ -253,7 +256,7 @@ [toolbar/content-title (i18n/label :t/transaction-details)] (when transaction [toolbar/actions (details-action hash url)])] [react/scroll-view {:style components.styles/main-container} - [details-header network transaction] + [details-header network all-tokens transaction] [details-confirmations confirmations confirmations-progress type] [react/view {:style styles/details-separator}] [details-list transaction]]])) diff --git a/src/status_im/utils/config.cljs b/src/status_im/utils/config.cljs index e777513a8d..ba2c8dc42b 100644 --- a/src/status_im/utils/config.cljs +++ b/src/status_im/utils/config.cljs @@ -31,6 +31,7 @@ (def extensions-enabled? (enabled? (get-config :EXTENSIONS 0))) (def hardwallet-enabled? (enabled? (get-config :HARDWALLET_ENABLED 0))) (def dev-build? (enabled? (get-config :DEV_BUILD 0))) +(def erc20-contract-warnings-enabled? (enabled? (get-config :ERC20_CONTRACT_WARNINGS))) ;; CONFIG VALUES (def log-level diff --git a/src/status_im/utils/core.cljc b/src/status_im/utils/core.cljc index 95fade193e..7ecf7fd561 100644 --- a/src/status_im/utils/core.cljc +++ b/src/status_im/utils/core.cljc @@ -45,7 +45,7 @@ (defn map-values "Efficiently apply function to all map values" - [m f] + [f m] (into {} (map (fn [[k v]] [k (f v)])) @@ -57,3 +57,9 @@ (if (every? map? maps) (apply merge-with deep-merge maps) (last maps))) + +(defn index-by + "Given a collection and a unique key function, returns a map that indexes the collection. + Similar to group-by except that the map values are single objects (depends on key uniqueness)." + [key coll] + (into {} (map #(vector (key %) %) coll))) diff --git a/src/status_im/utils/ethereum/eip681.cljs b/src/status_im/utils/ethereum/eip681.cljs index 5a5267af11..86bf1a8c54 100644 --- a/src/status_im/utils/ethereum/eip681.cljs +++ b/src/status_im/utils/ethereum/eip681.cljs @@ -65,7 +65,7 @@ n (money/bignumber (string/replace s "ETH" ""))] (if eth? (.times n 1e18) n)))) -(defn extract-request-details [{:keys [value address chain-id function-name function-arguments]}] +(defn extract-request-details [{:keys [value address chain-id function-name function-arguments]} all-tokens] "Return a map encapsulating request details (with keys `value`, `address` and `symbol`) from a parsed URI. Supports ethereum and erc20 token." (when address @@ -76,7 +76,7 @@ :address address} "transfer" {:value (money/bignumber (:uint256 function-arguments)) - :symbol (:symbol (tokens/address->token (ethereum/chain-id->chain-keyword chain-id) address)) + :symbol (:symbol (tokens/address->token all-tokens (ethereum/chain-id->chain-keyword chain-id) address)) :address (:address function-arguments)} nil))) @@ -104,8 +104,8 @@ (defn generate-erc20-uri "Generate a EIP 681 URI encapsulating ERC20 token transfer" - [address {:keys [symbol value chain-id] :as m}] - (when-let [token (tokens/symbol->token (if chain-id (ethereum/chain-id->chain-keyword chain-id) :mainnet) symbol)] + [address {:keys [symbol value chain-id] :as m} all-tokens] + (when-let [token (tokens/symbol->token all-tokens (if chain-id (ethereum/chain-id->chain-keyword chain-id) :mainnet) symbol)] (generate-uri (:address token) (merge (dissoc m :value :symbol) {:function-name "transfer" diff --git a/src/status_im/utils/ethereum/erc20.cljs b/src/status_im/utils/ethereum/erc20.cljs index a88d38cd3b..9822cea854 100644 --- a/src/status_im/utils/ethereum/erc20.cljs +++ b/src/status_im/utils/ethereum/erc20.cljs @@ -18,27 +18,99 @@ (:require [status-im.utils.ethereum.core :as ethereum] [status-im.native-module.core :as status] [status-im.utils.security :as security] + [status-im.js-dependencies :as dependencies] [status-im.utils.types :as types]) (:refer-clojure :exclude [name symbol])) +(def utils dependencies/web3-utils) + +(def abi + (clj->js + [{:constant true + :inputs [] + :name "name" + :outputs [{:name "" + :type "string"}] + :payable false + :stateMutability "view" + :type "function"} + {:constant true + :inputs [] + :name "symbol" + :outputs [{:name "" + :type "string"}] + :payable false + :stateMutability "view" + :type "function"} + {:constant true + :inputs [] + :name "decimals" + :outputs [{:name "" + :type "uint8"}] + :payable false + :stateMutability "view" + :type "function"} + {:constant true + :inputs [{:name "_who" + :type "address"}] + :name "balanceOf" + :outputs [{:name "" + :type "uint256"}] + :payable false + :stateMutability "view" + :type "function"} + {:constant true + :inputs [] + :name "totalSupply" + :outputs [{:name "" + :type "uint256"}], + :payable false + :stateMutability "view" + :type "function"} + {:constant false + :inputs [{:name "_to" + :type "address"} + {:name "_value" + :type "uint256"}] + :name "transfer" + :outputs [{:name "" + :type "bool"}], + :payable false + :stateMutability "nonpayable" + :type "function"} + {:anonymous false + :inputs [{:indexed true + :name "from" + :type "address"}, + {:indexed true + :name "to" + :type "address"}, + {:indexed false + :name "value" + :type "uint256"}] + :name "Transfer" + :type "event"}])) + +(defn get-instance* [web3 contract] + (.at (.contract (.-eth web3) abi) contract)) + +(def get-instance + (memoize get-instance*)) + (defn name [web3 contract cb] - (ethereum/call web3 (ethereum/call-params contract "name()") cb)) + (.name (get-instance web3 contract) cb)) (defn symbol [web3 contract cb] - (ethereum/call web3 (ethereum/call-params contract "symbol()") cb)) + (.symbol (get-instance web3 contract) cb)) (defn decimals [web3 contract cb] - (ethereum/call web3 (ethereum/call-params contract "decimals()") cb)) + (.decimals (get-instance web3 contract) cb)) (defn total-supply [web3 contract cb] - (ethereum/call web3 - (ethereum/call-params contract "totalSupply()") - #(cb %1 (ethereum/hex->bignumber %2)))) + (.totalSupply (get-instance web3 contract) cb)) (defn balance-of [web3 contract address cb] - (ethereum/call web3 - (ethereum/call-params contract "balanceOf(address)" (ethereum/normalized-address address)) - #(cb %1 (ethereum/hex->bignumber %2)))) + (.balanceOf (get-instance web3 contract) address cb)) (defn transfer [contract from to value gas gas-price masked-password on-completed] (status/send-transaction (types/clj->json diff --git a/src/status_im/utils/ethereum/tokens.cljs b/src/status_im/utils/ethereum/tokens.cljs index f92f4656ed..115b6f80ac 100644 --- a/src/status_im/utils/ethereum/tokens.cljs +++ b/src/status_im/utils/ethereum/tokens.cljs @@ -45,9 +45,21 @@ (defn ethereum? [symbol] (native-currency-symbols symbol)) -;; symbol are used as global identifier (per network) so they must be unique +;; NOTE(goranjovic) - fields description: +;; +;; - address - token contract address +;; - symbol - token identifier, must be unique within network +;; - name - token display name +;; - decimals - the maximum number of decimals (raw balance must be divided by 10^decimals to get the actual amount) +;; - nft? - set to true when token is an ERC-781 collectible +;; - hidden? - when true, token is not displayed in any asset selection screens, but will be displayed properly in +;; transaction history (setting this field is a form of "soft" token removal). +;; - skip-decimals-check? - some tokens do not include the decimals field, which is compliant with ERC-20 since it is +;;; and optional field. In that case we are explicitly skipping this step in order not to raise a false error. +;;; We have this explicit flag for decimals and not for name and symbol because we can't tell apart unset decimals +;;; from 0 decimals case. -(def all +(def all-default-tokens {:mainnet (resolve-icons :mainnet [{:symbol :DAI @@ -83,9 +95,9 @@ :address "0xB97048628DB6B661D4C2aA833e95Dbe1A905B280" :decimals 18} {:symbol :VRS - :name "VEROS" - :address "0xedbaf3c5100302dcdda53269322f3730b1f0416d" - :decimals 5} + :name "Veros" + :address "0x92E78dAe1315067a8819EFD6dCA432de9DCdE2e9" + :decimals 6} {:symbol :GNT :name "Golem Network Token" :address "0xa74476443119A942dE498590Fe1f2454d7D4aC0d" @@ -106,10 +118,11 @@ :name "Kyber Network Crystal" :address "0xdd974d5c2e2928dea5f71b9825b8b646686bd200" :decimals 18} - {:symbol :DGD - :name "Digix DAO" - :address "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a" - :decimals 9} + {:symbol :DGD + :name "Digix DAO" + :address "0xe0b7927c4af23765cb51314a0e0521a9645f0e2a" + :decimals 9 + :skip-decimals-check? true} {:symbol :AE :name "Aeternity" :address "0x5ca9a71b1d01849c0a95490cc00559717fcf0d1d" @@ -118,8 +131,8 @@ :name "Tronix" :address "0xf230b790e05390fc8295f4d3f60332c93bed42e2" :decimals 6} - {:symbol :BQX - :name "Bitquence" + {:symbol :ETHOS + :name "Ethos" :address "0x5af2be193a6abca9c8817001f45744777db30756" :decimals 8} {:symbol :RDN @@ -127,7 +140,7 @@ :address "0x255aa6df07540cb5d3d297f0d0d4d84cb52bc8e6" :decimals 18} {:symbol :SNT - :name "Status Network" + :name "Status Network Token" :address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :decimals 18} {:symbol :SNGLS @@ -202,16 +215,17 @@ :name "Monaco" :address "0xb63b606ac810a52cca15e44bb630fd42d8d1d83d" :decimals 8} - {:symbol :LRC - :name "loopring" - :address "0xEF68e7C694F40c8202821eDF525dE3782458639f" - :decimals 18} + {:symbol :LRC + :name "loopring" + :address "0xEF68e7C694F40c8202821eDF525dE3782458639f" + :decimals 18 + :skip-decimals-check? true} {:symbol :ZSC :name "Zeus Shield Coin" :address "0x7A41e0517a5ecA4FdbC7FbebA4D4c47B9fF6DC63" :decimals 18} {:symbol :DATA - :name "DATAcoin" + :name "Streamr DATAcoin" :address "0x0cf0ee63788a0849fe5297f3407f701e122cc023" :decimals 18} {:symbol :RCN @@ -271,7 +285,7 @@ :address "0x12480e24eb5bec1a9d4369cab6a80cad3c0a377a" :decimals 2} {:symbol :MANA - :name "Decentraland" + :name "Decentraland MANA" :address "0x0f5d2fb29fb7d3cfee444a200298f468908cc942" :decimals 18} {:symbol :AST @@ -283,7 +297,7 @@ :address "0x48f775efbe4f5ece6e0df2f7b5932df56823b990" :decimals 0} {:symbol :1ST - :name "Firstblood" + :name "FirstBlood Token" :address "0xaf30d2a7e90d7dc361c8c4585e9bb7d2f6f15bc7" :decimals 18} {:symbol :CFI @@ -343,7 +357,7 @@ :address "0xced4e93198734ddaff8492d525bd258d49eb388e" :decimals 18} {:symbol :CSNO - :name "BitDice CSNO" + :name "BitDice" :address "0x29d75277ac7f0335b2165d0895e8725cbf658d73" :decimals 8} {:symbol :COB @@ -375,11 +389,11 @@ :address "0x4DF812F6064def1e5e029f1ca858777CC98D2D81" :decimals 8} {:symbol :VIB - :name "VIB" + :name "Vibe" :address "0x2c974b2d0ba1716e644c1fc59982a89ddd2ff724" :decimals 18} {:symbol :PRG - :name "ParagonCoin" + :name "PRG" :address "0x7728dFEF5aBd468669EB7f9b48A7f70a501eD29D" :decimals 6} {:symbol :DPY @@ -395,7 +409,7 @@ :address "0x08f5a9235b08173b7569f83645d2c7fb55e8ccd8" :decimals 8} {:symbol :DRT - :name "Domraider" + :name "DomRaiderToken" :address "0x9af4f26941677c706cfecf6d3379ff01bb85d5ab" :decimals 8} {:symbol :SPANK @@ -418,13 +432,13 @@ :name "Simple Token" :address "0x2c4e8f2d746113d0696ce89b35f0d8bf88e0aeca" :decimals 18} - ;; NOTE(goranjovic) : the following three tokens are removed from the Manage Assets list + ;; NOTE(goranjovic): the following three tokens are removed from the Manage Assets list ;; and automatically removed from user's selection by a migration. However, we still need ;; them listed here in order to correctly display any previous transactions the user had ;; in their history prior to the upgrade. So, we're just hiding them, not actually deleting from the ;; app. - {:symbol :CTR - :name "Centra" + {:symbol :Centra + :name "Centra token" :address "0x96A65609a7B84E8842732DEB08f56C3E21aC6f8a" :decimals 18 :hidden? true} @@ -438,13 +452,14 @@ :address "0x9B11EFcAAA1890f6eE52C6bB7CF8153aC5d74139" :decimals 8 :hidden? true} + ;; NOTE(goranjovic): the following tokens are collectibles {:symbol :CK :nft? true :name "CryptoKitties" :address "0x06012c8cf97bead5deae237070f9587f8e7a266d"} {:symbol :EMONA :nft? true - :name "Etheremon" + :name "EtheremonAsset" :address "0xB2c0782ae4A299f7358758B2D15dA9bF29E1DD99"} {:symbol :STRK :nft? true @@ -452,7 +467,7 @@ :address "0xdcaad9fd9a74144d226dbf94ce6162ca9f09ed7e"} {:symbol :SUPR :nft? true - :name "SuperRare" + :name "SupeRare" :address "0x41a322b28d0ff354040e2cbc676f0320d8c8850d"} {:symbol :KDO :nft? true @@ -469,7 +484,7 @@ :symbol :HND :decimals 0 :address "0xdee43a267e8726efd60c2e7d5b81552dcd4fa35c"} - {:name "Lucky XS Test" + {:name "Lucky Test Token" :symbol :LXS :decimals 2 :address "0x703d7dc0bc8e314d65436adf985dda51e09ad43b"} @@ -497,37 +512,33 @@ :name "KudosToken" :address "0x93bb0afbd0627bbd3a6c72bc318341d3a22e254a"}]) - :custom []}) + :custom []}) (defn tokens-for "makes sure all addresses are lower-case TODO: token list should be speced and not accept non-lower-cased addresses" - [chain] - (mapv #(update % :address string/lower-case) (get all chain))) + [all-tokens chain] + (mapv #(update % :address string/lower-case) (vals (get all-tokens chain)))) -(defn all-assets-for [chain] - (concat [(native-currency chain)] - (tokens-for chain))) +(defn nfts-for [all-tokens chain] + (filter :nft? (tokens-for all-tokens chain))) -(defn nfts-for [chain] - (filter :nft? (tokens-for chain))) - -(defn sorted-tokens-for [chain] - (->> (tokens-for chain) +(defn sorted-tokens-for [all-tokens chain] + (->> (tokens-for all-tokens chain) (filter #(not (:hidden? %))) (sort #(compare (string/lower-case (:name %1)) (string/lower-case (:name %2)))))) -(defn symbol->token [chain symbol] - (some #(when (= symbol (:symbol %)) %) (tokens-for chain))) +(defn symbol->token [all-tokens chain symbol] + (some #(when (= symbol (:symbol %)) %) (tokens-for all-tokens chain))) -(defn address->token [chain address] +(defn address->token [all-tokens chain address] (some #(when (= (string/lower-case address) - (string/lower-case (:address %))) %) (tokens-for chain))) + (string/lower-case (:address %))) %) (tokens-for all-tokens chain))) -(defn asset-for [chain symbol] +(defn asset-for [all-tokens chain symbol] (let [native-coin (native-currency chain)] (if (or (= (:symbol-display native-coin) symbol) (= (:symbol native-coin) symbol)) native-coin - (symbol->token chain symbol)))) + (symbol->token all-tokens chain symbol)))) diff --git a/test/cljs/status_im/test/chat/commands/impl/transactions.cljs b/test/cljs/status_im/test/chat/commands/impl/transactions.cljs index 002cd56b1b..86bb345c18 100644 --- a/test/cljs/status_im/test/chat/commands/impl/transactions.cljs +++ b/test/cljs/status_im/test/chat/commands/impl/transactions.cljs @@ -10,7 +10,11 @@ :current-chat-id "recipient" :contacts/contacts {"recipient" {:name "Recipient" :address "0xAA" - :public-key "0xBB"}}}}) + :public-key "0xBB"}} + :wallet/all-tokens {:mainnet {"0x744d70fdbe2ba4cf95131626614a1763df805b9e" {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" + :name "Status Network Token" + :symbol :SNT + :decimals 18}}}}}) ;; testing the `/send` command diff --git a/test/cljs/status_im/test/utils/ethereum/eip681.cljs b/test/cljs/status_im/test/utils/ethereum/eip681.cljs index 694ddb028f..10001db883 100644 --- a/test/cljs/status_im/test/utils/ethereum/eip681.cljs +++ b/test/cljs/status_im/test/utils/ethereum/eip681.cljs @@ -29,16 +29,26 @@ (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1 :function-name "transfer" :gas "100" :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 "1"}} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1&gas=100")))) +(def all-tokens + {:mainnet {"0x744d70fdbe2ba4cf95131626614a1763df805b9e" {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" + :name "Status Network Token" + :symbol :SNT + :decimals 18}} + :testnet {"0xc55cF4B03948D7EBc8b9E8BAD92643703811d162" {:address "0xc55cF4B03948D7EBc8b9E8BAD92643703811d162" + :name "Status Test Token" + :symbol :STT + :decimals 18}}}) + (deftest generate-erc20-uri - (is (= nil (eip681/generate-erc20-uri nil nil))) + (is (= nil (eip681/generate-erc20-uri nil nil all-tokens))) (is (= "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" - (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :value 5}))) + (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :value 5} all-tokens))) (is (= "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7&gas=10000&gasPrice=10000" - (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :value 5 :gas 10000 :gasPrice 10000}))) + (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :value 5 :gas 10000 :gasPrice 10000} all-tokens))) (is (= "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" - (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :chain-id 1 :value 5}))) + (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :chain-id 1 :value 5} all-tokens))) (is (= "ethereum:0xc55cf4b03948d7ebc8b9e8bad92643703811d162@3/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" - (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :STT :chain-id 3 :value 5})))) + (eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :STT :chain-id 3 :value 5} all-tokens)))) (deftest generate-uri (is (= nil (eip681/generate-uri nil nil))) @@ -73,13 +83,17 @@ (is (.equals (money/bignumber "111122223333441239") (eip681/parse-eth-value "111122223333441239")))) (deftest extract-request-details - (let [{:keys [value symbol address]} (eip681/extract-request-details {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1ETH"})] + (let [{:keys [value symbol address]} (eip681/extract-request-details {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1ETH"} {})] (is (.equals (money/ether->wei (money/bignumber 1)) value)) (is (= :ETH symbol)) (is (= "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" address))) - (is (nil? (eip681/extract-request-details {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :chain-id 1 :function-name "unknown"}))) + (is (nil? (eip681/extract-request-details {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :chain-id 1 :function-name "unknown"} {}))) (let [{:keys [value symbol address]} (eip681/extract-request-details {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" :chain-id 1 - :function-name "transfer" :function-arguments {:uint256 1000 :address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}})] + :function-name "transfer" :function-arguments {:uint256 1000 :address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"}} + {:mainnet {"0x744d70fdbe2ba4cf95131626614a1763df805b9e" {:address "0x744d70fdbe2ba4cf95131626614a1763df805b9e" + :name "Status Network Token" + :symbol :SNT + :decimals 18}}})] (is (.equals (money/bignumber 1000) value)) (is (= :SNT symbol)) (is (= "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" address)))) diff --git a/test/cljs/status_im/test/utils/utils.cljs b/test/cljs/status_im/test/utils/utils.cljs index 51b1df36af..437ef7c671 100644 --- a/test/cljs/status_im/test/utils/utils.cljs +++ b/test/cljs/status_im/test/utils/utils.cljs @@ -30,9 +30,9 @@ (is (= {:a 0} (u/update-if-present {:a 0} :b inc)))) (deftest map-values-test - (is (= {} (u/map-values {} inc))) - (is (= {:a 1} (u/map-values {:a 0} inc))) - (is (= {:a 1 :b 2} (u/map-values {:a 0 :b 1} inc)))) + (is (= {} (u/map-values inc {}))) + (is (= {:a 1} (u/map-values inc {:a 0}))) + (is (= {:a 1 :b 2} (u/map-values inc {:a 0 :b 1})))) (deftest deep-merge-test (is (= {} (u/deep-merge {} {}))) diff --git a/translations/en.json b/translations/en.json index 6e49d97bdd..bf65704c6d 100644 --- a/translations/en.json +++ b/translations/en.json @@ -2,6 +2,7 @@ "validation-amount-invalid-number": "Amount is not a valid number", "transaction-details": "Transaction details", "confirm": "Confirm", + "warning": "Warning", "public-chat": "Public chat", "description": "Description", "devices": "Devices", @@ -350,6 +351,9 @@ "sharing-copy-to-clipboard": "Copy to clipboard", "your-wallets": "Your wallets", "phone-international": "International 2", + "token-auto-validate-name-error": "Wrong name for token {{symbol}} at address {{address}} - set to {{expected}} but detected as {{actual}}", + "token-auto-validate-symbol-error": "Wrong symbol for token {{symbol}} at address {{address}} - set to {{expected}} but detected as {{actual}}", + "token-auto-validate-decimals-error": "Wrong decimals for token {{symbol}} at address {{address}} - set to {{expected}} but detected as {{actual}}", "error-unable-to-get-token-balance": "Unable to get token balance", "error-cant-send-transaction-offline": "Can't send transaction in offline mode", "error-cant-sign-message-offline": "Can't sign message in offline mode",