[#18736] add address to watch using an ENS (#19043)

* Add capability to register an address based on an ENS

* update tests

* Check for existing addresses using an ENS
This commit is contained in:
Ulises Manuel 2024-03-08 12:20:41 -06:00 committed by GitHub
parent 7a6714eeb2
commit 3c6923963d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 179 additions and 122 deletions

View File

@ -0,0 +1,54 @@
(ns status-im.contexts.wallet.accounts.add-account.address-to-watch.events
(:require [clojure.string :as string]
[status-im.constants :as constants]
[taoensso.timbre :as log]
[utils.re-frame :as rf]))
(rf/reg-event-fx
:wallet/ens-not-found
(fn [{:keys [db]} _]
{:db (-> db
(assoc-in [:wallet :ui :add-address-to-watch :activity-state] :invalid-ens)
(assoc-in [:wallet :ui :add-address-to-watch :validated-address] nil))}))
(rf/reg-event-fx
:wallet/store-address-activity
(fn [{:keys [db]} [address {:keys [hasActivity]}]]
(let [registered-addresses (-> db :wallet :accounts keys set)
address-already-registered? (registered-addresses address)]
(if address-already-registered?
{:db (-> db
(assoc-in [:wallet :ui :add-address-to-watch :activity-state]
:address-already-registered)
(assoc-in [:wallet :ui :add-address-to-watch :validated-address] nil))}
(let [state (if hasActivity :has-activity :no-activity)]
{:db (-> db
(assoc-in [:wallet :ui :add-address-to-watch :activity-state] state)
(assoc-in [:wallet :ui :add-address-to-watch :validated-address] address))})))))
(rf/reg-event-fx
:wallet/clear-address-activity
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui] dissoc :add-address-to-watch)}))
(rf/reg-event-fx
:wallet/get-address-details
(fn [{:keys [db]} [address-or-ens]]
(let [request-params [constants/ethereum-mainnet-chain-id address-or-ens]
ens? (string/includes? address-or-ens ".")]
{:db (-> db
(assoc-in [:wallet :ui :add-address-to-watch :activity-state] :scanning)
(assoc-in [:wallet :ui :add-address-to-watch :validated-address] nil))
:fx [(if ens?
[:json-rpc/call
[{:method "ens_addressOf"
:params request-params
:on-success [:wallet/get-address-details]
:on-error [:wallet/ens-not-found]}]]
[:json-rpc/call
[{:method "wallet_getAddressDetails"
:params request-params
:on-success [:wallet/store-address-activity address-or-ens]
:on-error #(log/info "failed to get address details"
{:error %
:event :wallet/get-address-details})}]])]})))

View File

@ -8,22 +8,22 @@
[status-im.common.floating-button-page.view :as floating-button-page]
[status-im.contexts.wallet.add-address-to-watch.style :as style]
[status-im.contexts.wallet.common.validation :as validation]
[status-im.subs.wallet.add-account.address-to-watch]
[utils.debounce :as debounce]
[utils.i18n :as i18n]
[utils.re-frame :as rf]))
(defn validate-message
[addresses]
(fn [s]
(cond
(or (= s nil) (= s "")) nil
(contains? addresses s) (i18n/label :t/address-already-in-use)
(not (or (validation/eth-address? s)
(validation/ens-name? s))) (i18n/label :t/invalid-address)
:else nil)))
(defn- validate-address
[known-addresses user-input]
(cond
(or (nil? user-input) (= user-input "")) nil
(contains? known-addresses user-input) (i18n/label :t/address-already-in-use)
(not
(or (validation/eth-address? user-input)
(validation/ens-name? user-input))) (i18n/label :t/invalid-address)))
(defn- address-input
[{:keys [input-value validation-msg validate
clear-input]}]
[{:keys [input-value validation-msg validate clear-input]}]
(let [scanned-address (rf/sub [:wallet/scanned-address])
empty-input? (and (string/blank? @input-value)
(string/blank? scanned-address))
@ -32,10 +32,11 @@
(reset! input-value new-text)
(reagent/flush)
(if (and (not-empty new-text) (nil? (validate new-text)))
(rf/dispatch [:wallet/get-address-details new-text])
(rf/dispatch [:wallet/clear-address-activity-check]))
(debounce/debounce-and-dispatch [:wallet/get-address-details new-text]
500)
(rf/dispatch [:wallet/clear-address-activity]))
(when (and scanned-address (not= scanned-address new-text))
(rf/dispatch [:wallet/clear-address-activity-check])
(rf/dispatch [:wallet/clear-address-activity])
(rf/dispatch [:wallet/clean-scanned-address])))
paste-on-input #(clipboard/get-string
(fn [clipboard-text]
@ -44,8 +45,7 @@
(when-not (string/blank? scanned-address)
(on-change-text scanned-address)))
[scanned-address])
[rn/view
{:style style/input-container}
[rn/view {:style style/input-container}
[quo/input
{:accessibility-label :add-address-to-watch
:placeholder (i18n/label :t/address-placeholder)
@ -72,84 +72,92 @@
:i/scan]]))
(defn activity-indicator
[]
(let [activity-state (rf/sub [:wallet/watch-address-activity-state])
{:keys [accessibility-label icon type message]}
(case activity-state
:has-activity {:accessibility-label :account-has-activity
:icon :i/done
:type :success
:message :t/this-address-has-activity}
:no-activity {:accessibility-label :account-has-no-activity
:icon :i/info
:type :warning
:message :t/this-address-has-no-activity}
{:accessibility-label :searching-for-activity
:icon :i/pending-state
:type :default
:message :t/searching-for-activity})]
[activity-state]
(let [{:keys [message]
:as props} (case activity-state
:has-activity {:accessibility-label :account-has-activity
:icon :i/done
:type :success
:message :t/this-address-has-activity}
:no-activity {:accessibility-label :account-has-no-activity
:icon :i/info
:type :warning
:message :t/this-address-has-no-activity}
:invalid-ens {:accessibility-label :error-message
:icon :i/info
:type :error
:message :t/invalid-address}
:address-already-registered {:accessibility-label :error-message
:icon :i/info
:type :error
:message :t/address-already-in-use}
{:accessibility-label :searching-for-activity
:icon :i/pending-state
:type :default
:message :t/searching-for-activity})]
(when activity-state
[quo/info-message
{:accessibility-label accessibility-label
:size :default
:icon icon
:type type
:style style/info-message}
(assoc props
:style style/info-message
:size :default)
(i18n/label message)])))
(defn view
[]
(let [addresses (rf/sub [:wallet/addresses])
input-value (reagent/atom nil)
validate (validate-message (set addresses))
validation-msg (reagent/atom (validate
@input-value))
validate #(validate-address (set addresses) %)
validation-msg (reagent/atom nil)
clear-input (fn []
(reset! input-value nil)
(reset! validation-msg nil)
(rf/dispatch [:wallet/clear-address-activity-check])
(rf/dispatch [:wallet/clear-address-activity])
(rf/dispatch [:wallet/clean-scanned-address]))
customization-color (rf/sub [:profile/customization-color])]
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:wallet/clear-address-activity-check])
(rf/dispatch [:wallet/clear-address-activity])
(fn []
[rn/view
{:style {:flex 1}}
[floating-button-page/view
{:header [quo/page-nav
{:type :no-title
:icon-name :i/close
:on-press (fn []
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:wallet/clear-address-activity-check])
(rf/dispatch [:navigate-back]))}]
:footer [quo/button
{:customization-color customization-color
:disabled? (or (string/blank? @input-value)
(some? (validate @input-value)))
:on-press (fn []
(rf/dispatch [:navigate-to
:confirm-address-to-watch
{:address @input-value}])
(clear-input))
:container-style {:z-index 2}}
(i18n/label :t/continue)]}
[quo/page-top
{:container-style style/header-container
:title (i18n/label :t/add-address)
:description :text
:description-text (i18n/label :t/enter-eth)}]
[:f> address-input
{:input-value input-value
:validate validate
:validation-msg validation-msg
:clear-input clear-input}]
(when @validation-msg
[quo/info-message
{:accessibility-label :error-message
:size :default
:icon :i/info
:type :error
:style style/info-message}
@validation-msg])
[activity-indicator]]])))
(let [activity-state (rf/sub [:wallet/watch-address-activity-state])
validated-address (rf/sub [:wallet/watch-address-validated-address])]
[rn/view {:style {:flex 1}}
[floating-button-page/view
{:header [quo/page-nav
{:type :no-title
:icon-name :i/close
:on-press (fn []
(rf/dispatch [:wallet/clean-scanned-address])
(rf/dispatch [:wallet/clear-address-activity])
(rf/dispatch [:navigate-back]))}]
:footer [quo/button
{:customization-color customization-color
:disabled? (or (string/blank? @input-value)
(some? (validate @input-value))
(= activity-state :invalid-ens)
(= activity-state :scanning)
(not validated-address))
:on-press (fn []
(rf/dispatch [:navigate-to
:confirm-address-to-watch
{:address validated-address}])
(clear-input))
:container-style {:z-index 2}}
(i18n/label :t/continue)]}
[quo/page-top
{:container-style style/header-container
:title (i18n/label :t/add-address)
:description :text
:description-text (i18n/label :t/enter-eth)}]
[:f> address-input
{:input-value input-value
:validate validate
:validation-msg validation-msg
:clear-input clear-input}]
(if @validation-msg
[quo/info-message
{:accessibility-label :error-message
:size :default
:icon :i/info
:type :error
:style style/info-message}
@validation-msg]
[activity-indicator activity-state])]]))))

View File

@ -4,13 +4,13 @@
[react-native.background-timer :as background-timer]
[react-native.platform :as platform]
[status-im.constants :as constants]
[status-im.contexts.wallet.accounts.add-account.address-to-watch.events]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.contexts.wallet.data-store :as data-store]
[status-im.contexts.wallet.events.collectibles]
[status-im.contexts.wallet.item-types :as item-types]
[taoensso.timbre :as log]
[utils.collection]
[utils.ethereum.chain :as chain]
[utils.ethereum.eip.eip55 :as eip55]
[utils.i18n :as i18n]
[utils.number]
@ -360,27 +360,6 @@
(fn [{:keys [db]}]
{:db (assoc db :wallet/valid-ens-or-address? false)}))
(rf/reg-event-fx :wallet/get-address-details-success
(fn [{:keys [db]} [{:keys [hasActivity]}]]
{:db (assoc-in db
[:wallet :ui :watch-address-activity-state]
(if hasActivity :has-activity :no-activity))}))
(rf/reg-event-fx :wallet/clear-address-activity-check
(fn [{:keys [db]}]
{:db (update-in db [:wallet :ui] dissoc :watch-address-activity-state)}))
(rf/reg-event-fx :wallet/get-address-details
(fn [{:keys [db]} [address]]
{:db (assoc-in db [:wallet :ui :watch-address-activity-state] :scanning)
:fx [[:json-rpc/call
[{:method "wallet_getAddressDetails"
:params [(chain/chain-id db) address]
:on-success [:wallet/get-address-details-success]
:on-error #(log/info "failed to get address details"
{:error %
:event :wallet/get-address-details})}]]]}))
(rf/reg-event-fx
:wallet/navigate-to-chain-explorer-from-bottom-sheet
(fn [_ [explorer-link address]]

View File

@ -115,16 +115,15 @@
(defn- local-suggestions-list
[]
(fn []
(let [local-suggestion (rf/sub [:wallet/local-suggestions])]
[rn/view {:style {:flex 1}}
[rn/flat-list
{:data local-suggestion
:content-container-style {:flex-grow 1}
:key-fn :id
:on-scroll-to-index-failed identity
:keyboard-should-persist-taps :handled
:render-fn suggestion-component}]])))
(let [local-suggestion (rf/sub [:wallet/local-suggestions])]
[rn/view {:style {:flex 1}}
[rn/flat-list
{:data local-suggestion
:content-container-style {:flex-grow 1}
:key-fn :id
:on-scroll-to-index-failed identity
:keyboard-should-persist-taps :handled
:render-fn suggestion-component}]]))
(defn- f-view
[]

View File

@ -0,0 +1,17 @@
(ns status-im.subs.wallet.add-account.address-to-watch
(:require [re-frame.core :as rf]))
(rf/reg-sub
:wallet/add-address-to-watch
:<- [:wallet/ui]
:-> :add-address-to-watch)
(rf/reg-sub
:wallet/watch-address-activity-state
:<- [:wallet/add-address-to-watch]
:-> :activity-state)
(rf/reg-sub
:wallet/watch-address-validated-address
:<- [:wallet/add-address-to-watch]
:-> :validated-address)

View File

@ -2,6 +2,7 @@
(:require [clojure.string :as string]
[re-frame.core :as rf]
[status-im.contexts.wallet.common.utils :as utils]
[status-im.subs.wallet.add-account.address-to-watch]
[utils.number]))
(defn- filter-networks
@ -93,11 +94,6 @@
:<- [:wallet/wallet-send]
:-> :bridge-to-chain-id)
(rf/reg-sub
:wallet/watch-address-activity-state
:<- [:wallet/ui]
:-> :watch-address-activity-state)
(rf/reg-sub
:wallet/keypairs
:<- [:wallet]

View File

@ -296,12 +296,16 @@
(is (nil? (rf/sub [sub-name]))))
(testing "watch address activity state with no-activity value"
(swap! rf-db/app-db #(assoc-in % [:wallet :ui :watch-address-activity-state] :no-activity))
(swap! rf-db/app-db #(assoc-in % [:wallet :ui :add-address-to-watch :activity-state] :no-activity))
(is (match? :no-activity (rf/sub [sub-name]))))
(testing "watch address activity state with has-activity value"
(swap! rf-db/app-db #(assoc-in % [:wallet :ui :watch-address-activity-state] :has-activity))
(is (match? :has-activity (rf/sub [sub-name])))))
(swap! rf-db/app-db #(assoc-in % [:wallet :ui :add-address-to-watch :activity-state] :has-activity))
(is (match? :has-activity (rf/sub [sub-name]))))
(testing "watch address activity state with invalid-ens value"
(swap! rf-db/app-db #(assoc-in % [:wallet :ui :add-address-to-watch :activity-state] :invalid-ens))
(is (match? :invalid-ens (rf/sub [sub-name])))))
(h/deftest-sub :wallet/accounts-without-current-viewing-account
[sub-name]