Skip to content

Commit

Permalink
[#4772] Resolve ENS domains in browser
Browse files Browse the repository at this point in the history
  • Loading branch information
flexsurfer committed Aug 30, 2018
1 parent 869d64a commit c86fbb3
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 30 deletions.
3 changes: 2 additions & 1 deletion deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
com.andrewmcveigh/cljs-time {:mvn/version "0.5.2"}
com.taoensso/timbre {:mvn/version "4.10.0"}
hickory {:mvn/version "0.7.1"}
com.cognitect/transit-cljs {:mvn/version "0.8.248"}}
com.cognitect/transit-cljs {:mvn/version "0.8.248"}
mvxcvi/multihash {:mvn/version "2.0.3"}}

:aliases
{:dev {:extra-deps
Expand Down
3 changes: 2 additions & 1 deletion project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
[com.andrewmcveigh/cljs-time "0.5.2"]
[com.taoensso/timbre "4.10.0"]
[hickory "0.7.1"]
[com.cognitect/transit-cljs "0.8.248"]]
[com.cognitect/transit-cljs "0.8.248"]
[mvxcvi/multihash "2.0.3"]]
:plugins [[lein-cljsbuild "1.1.7"]
[lein-re-frisk "0.5.8"]
[lein-cljfmt "0.5.7"]
Expand Down
53 changes: 44 additions & 9 deletions src/status_im/models/browser.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@
[status-im.data-store.dapp-permissions :as dapp-permissions]
[status-im.i18n :as i18n]
[status-im.ui.screens.browser.default-dapps :as default-dapps]
[status-im.utils.http :as http]))
[status-im.utils.http :as http]
[clojure.string :as string]
[status-im.utils.ethereum.resolver :as resolver]
[status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.ens :as ens]
[multihash.core :as multihash]
[status-im.utils.handlers-macro :as handlers-macro]))

(defn get-current-url [{:keys [history history-index]}]
(when (and history-index history)
Expand All @@ -24,13 +30,13 @@
(assoc browser :dapp? true :name (:name dapp))
(assoc browser :dapp? false :name (i18n/label :t/browser)))))

(defn update-browser-fx [{:keys [db now]} browser]
(defn update-browser-fx [browser {:keys [db now]}]
(let [updated-browser (check-if-dapp-in-list (assoc browser :timestamp now))]
{:db (update-in db [:browser/browsers (:browser-id updated-browser)]
merge updated-browser)
:data-store/tx [(browser-store/save-browser-tx updated-browser)]}))

(defn update-browser-history-fx [cofx browser url loading?]
(defn update-browser-history-fx [browser url loading? cofx]
(when-not loading?
(let [history-index (:history-index browser)
history (:history browser)
Expand All @@ -43,12 +49,41 @@
new-index (if slash?
history-index
(dec (count new-history)))]
(update-browser-fx cofx
(assoc browser :history new-history :history-index new-index)))))))

(defn update-browser-and-navigate [cofx browser]
(merge (update-browser-fx cofx browser)
{:dispatch [:navigate-to :browser (:browser-id browser)]}))
(update-browser-fx (assoc browser :history new-history :history-index new-index)
cofx))))))

(defn ens? [host]
(string/ends-with? host ".eth"))

(defn ens-multihash-callback [hex]
(let [hash (when hex (multihash/base58 (multihash/create :sha2-256 (subs hex 2))))]
(if (and hash (not= hash resolver/default-hash))
(re-frame/dispatch [:ens-multihash-resolved hash])
(re-frame/dispatch [:update-browser-options {:resolving? false}]))))

(defn resolve-multihash-fx [host loading error? {{:keys [web3 network] :as db} :db}]
(let [network (get-in db [:account/account :networks network])
chain (ethereum/network->chain-keyword network)]
(if (and (not loading) (not error?) (ens? host))
{:db (assoc-in db [:browser/options :resolving?] true)
:resolve-ens-multihash {:web3 web3
:registry (get ens/ens-registries
chain)
:ens-name host
:cb ens-multihash-callback}}
{})))

(defn update-new-browser-and-navigate [host browser cofx]
(handlers-macro/merge-fx
cofx
{:dispatch [:navigate-to :browser {:browser-id (:browser-id browser)
:resolving? (ens? host)}]}
(update-browser-fx browser)
(resolve-multihash-fx host false false)))

(defn update-browser-and-navigate [browser cofx]
(merge (update-browser-fx browser cofx)
{:dispatch [:navigate-to :browser {:browser-id (:browser-id browser)}]}))

(def permissions {constants/dapp-permission-contact-code {:title (i18n/label :t/wants-to-access-profile)
:description (i18n/label :t/your-contact-code)
Expand Down
2 changes: 2 additions & 0 deletions src/status_im/ui/screens/browser/db.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
(spec/def :browser/history (spec/nilable vector?))
(spec/def :browser/history-index (spec/nilable int?))
(spec/def :browser/loading? (spec/nilable boolean?))
(spec/def :browser/resolving? (spec/nilable boolean?))
(spec/def :browser/url-editing? (spec/nilable boolean?))
(spec/def :browser/show-tooltip (spec/nilable keyword?))
(spec/def :browser/show-permission (spec/nilable map?))
Expand All @@ -19,6 +20,7 @@
(allowed-keys
:opt-un [:browser/browser-id
:browser/loading?
:browser/resolving?
:browser/url-editing?
:browser/show-tooltip
:browser/show-permission
Expand Down
48 changes: 38 additions & 10 deletions src/status_im/ui/screens/browser/events.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
[status-im.utils.random :as random]
[status-im.utils.types :as types]
[status-im.utils.universal-links.core :as utils.universal-links]
[taoensso.timbre :as log]))
[taoensso.timbre :as log]
[status-im.utils.ethereum.resolver :as resolver]))

(re-frame/reg-fx
:browse
Expand All @@ -39,19 +40,42 @@
(fn [[message webview]]
(.sendToBridge webview (types/clj->json message))))

(re-frame/reg-fx
:resolve-ens-multihash
(fn [{:keys [web3 registry ens-name cb]}]
(resolver/content web3 registry ens-name cb)))

(handlers/register-handler-fx
:browse-link-from-message
(fn [_ [_ link]]
{:browse link}))

(handlers/register-handler-fx
:ens-multihash-resolved
[re-frame/trim-v]
(fn [{:keys [db] :as cofx} [hash]]
(let [options (:browser/options db)
browsers (:browser/browsers db)
browser (get browsers (:browser-id options))
history-index (:history-index browser)]
(handlers-macro/merge-fx
cofx
{:db (assoc-in db [:browser/options :resolving?] false)}
(model/update-browser-fx
(assoc-in browser [:history history-index] (str "https://ipfs.infura.io/ipfs/" hash)))))))

(handlers/register-handler-fx
:open-url-in-browser
[re-frame/trim-v]
(fn [cofx [url]]
(let [normalized-url (http/normalize-and-decode-url url)]
(model/update-browser-and-navigate cofx {:browser-id (or (http/url-host normalized-url) (random/id))
:history-index 0
:history [normalized-url]}))))
(let [normalized-url (http/normalize-and-decode-url url)
host (http/url-host normalized-url)]
(model/update-new-browser-and-navigate
host
{:browser-id (or host (random/id))
:history-index 0
:history [normalized-url]}
cofx))))

(handlers/register-handler-fx
:send-to-bridge
Expand All @@ -63,13 +87,17 @@
:open-browser
[re-frame/trim-v]
(fn [cofx [browser]]
(model/update-browser-and-navigate cofx browser)))
(model/update-browser-and-navigate browser cofx)))

(handlers/register-handler-fx
:update-browser-on-nav-change
[re-frame/trim-v]
(fn [cofx [browser url loading]]
(model/update-browser-history-fx cofx browser url loading)))
(fn [cofx [browser url loading error?]]
(let [host (http/url-host url)]
(handlers-macro/merge-fx
cofx
(model/resolve-multihash-fx host loading error?)
(model/update-browser-history-fx browser url loading)))))

(handlers/register-handler-fx
:update-browser-options
Expand All @@ -85,7 +113,7 @@
:data-store/tx [(browser-store/remove-browser-tx browser-id)]}))

(defn nav-update-browser [cofx browser history-index]
(model/update-browser-fx cofx (assoc browser :history-index history-index)))
(model/update-browser-fx (assoc browser :history-index history-index) cofx))

(handlers/register-handler-fx
:browser-nav-back
Expand Down Expand Up @@ -113,7 +141,7 @@
(cond

(and (= type constants/history-state-changed) platform/ios? (not= "about:blank" url))
(model/update-browser-history-fx cofx browser url false)
(model/update-browser-history-fx browser url false cofx)

(= type constants/web3-send-async)
(model/web3-send-async payload messageId cofx)
Expand Down
4 changes: 2 additions & 2 deletions src/status_im/ui/screens/browser/navigation.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
(:require [status-im.ui.screens.navigation :as navigation]))

(defmethod navigation/preload-data! :browser
[db [_ _ browser-id]]
(assoc db :browser/options {:browser-id browser-id}))
[db [_ _ options]]
(assoc db :browser/options options))
13 changes: 7 additions & 6 deletions src/status_im/ui/screens/browser/views.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
(re-frame/dispatch [:update-browser-on-nav-change
browser
(http/normalize-and-decode-url @url-text)
false
false]))
:placeholder (i18n/label :t/enter-url)
:auto-capitalize :none
Expand Down Expand Up @@ -81,12 +82,12 @@
[react/text {:style styles/web-view-error-text}
(str desc)]]))

(defn on-navigation-change [event browser]
(defn on-navigation-change [event browser error?]
(let [{:strs [url loading]} (js->clj event)]
(when platform/ios?
(re-frame/dispatch [:update-browser-options {:loading? loading}]))
(when (not= "about:blank" url)
(re-frame/dispatch [:update-browser-on-nav-change browser url loading]))))
(re-frame/dispatch [:update-browser-on-nav-change browser url loading error?]))))

(defn get-inject-js [url]
(let [domain-name (nth (re-find #"^\w+://(www\.)?([^/:]+)" url) 2)]
Expand Down Expand Up @@ -115,7 +116,7 @@
(views/letsubs [webview (atom nil)
{:keys [address]} [:get-current-account]
{:keys [browser-id dapp? name] :as browser} [:get-current-browser]
{:keys [error? loading? url-editing? show-tooltip show-permission]} [:get :browser/options]
{:keys [error? loading? url-editing? show-tooltip show-permission resolving?]} [:get :browser/options]
rpc-url [:get :rpc-url]
network-id [:get-network-id]]
(let [can-go-back? (model/can-go-back? browser)
Expand All @@ -131,12 +132,12 @@
:ref #(do
(reset! webview %)
(re-frame/dispatch [:set :webview-bridge %]))
:source {:uri url}
:source (when-not resolving? {:uri url})
:java-script-enabled true
:bounces false
:local-storage-enabled true
:render-error web-view-error
:on-navigation-state-change #(on-navigation-change % browser)
:on-navigation-state-change #(on-navigation-change % browser error?)
:on-bridge-message #(re-frame/dispatch [:on-bridge-message %])
:on-load #(re-frame/dispatch [:update-browser-options {:error? false}])
:on-error #(re-frame/dispatch [:update-browser-options {:error? true
Expand All @@ -148,7 +149,7 @@
(ethereum/normalized-address address)
(str network-id)))
:injected-java-script js-res/webview-js}]
(when loading?
(when (or loading? resolving?)
[react/view styles/web-view-loading
[components/activity-indicator {:animating true}]])]
[navigation webview browser can-go-back? can-go-forward?]
Expand Down
2 changes: 1 addition & 1 deletion src/status_im/ui/screens/db.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@
(spec/def :navigation/prev-view-id (spec/nilable keyword?))
;; navigation screen params
(spec/def :navigation.screen-params/network-details (allowed-keys :req [:networks/selected-network]))
(spec/def :navigation.screen-params/browser (spec/nilable string?))
(spec/def :navigation.screen-params/browser (spec/nilable map?))
(spec/def :navigation.screen-params.profile-qr-viewer/contact (spec/nilable map?))
(spec/def :navigation.screen-params.profile-qr-viewer/source (spec/nilable keyword?))
(spec/def :navigation.screen-params.profile-qr-viewer/value (spec/nilable string?))
Expand Down
16 changes: 16 additions & 0 deletions src/status_im/utils/ethereum/resolver.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
(ns status-im.utils.ethereum.resolver
(:require [status-im.utils.ethereum.ens :as ens]))

(def resolver-abi (js* "[{'constant': true, 'inputs': [{'name': 'interfaceID', 'type': 'bytes4'}], 'name': 'supportsInterface', 'outputs': [{'name': '', 'type': 'bool'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentTypes', 'type': 'uint256'}], 'name': 'ABI', 'outputs': [{'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'name': 'setPubkey', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'content', 'outputs': [{'name': 'ret', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'addr', 'outputs': [{'name': 'ret', 'type': 'address'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'contentType', 'type': 'uint256'}, {'name': 'data', 'type': 'bytes'}], 'name': 'setABI', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'name', 'outputs': [{'name': 'ret', 'type': 'string'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'name', 'type': 'string'}], 'name': 'setName', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'hash', 'type': 'bytes32'}], 'name': 'setContent', 'outputs': [], 'payable': false, 'type': 'function'}, {'constant': true, 'inputs': [{'name': 'node', 'type': 'bytes32'}], 'name': 'pubkey', 'outputs': [{'name': 'x', 'type': 'bytes32'}, {'name': 'y', 'type': 'bytes32'}], 'payable': false, 'type': 'function'}, {'constant': false, 'inputs': [{'name': 'node', 'type': 'bytes32'}, {'name': 'addr', 'type': 'address'}], 'name': 'setAddr', 'outputs': [], 'payable': false, 'type': 'function'}, {'inputs': [{'name': 'ensAddr', 'type': 'address'}], 'payable': false, 'type': 'constructor'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'a', 'type': 'address'}], 'name': 'AddrChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'hash', 'type': 'bytes32'}], 'name': 'ContentChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'name', 'type': 'string'}], 'name': 'NameChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': true, 'name': 'contentType', 'type': 'uint256'}], 'name': 'ABIChanged', 'type': 'event'}, {'anonymous': false, 'inputs': [{'indexed': true, 'name': 'node', 'type': 'bytes32'}, {'indexed': false, 'name': 'x', 'type': 'bytes32'}, {'indexed': false, 'name': 'y', 'type': 'bytes32'}], 'name': 'PubkeyChanged', 'type': 'event'}]"))
(def default-address "0x0000000000000000000000000000000000000000")
(def default-hash "0x0000000000000000000000000000000000000000000000000000000000000000")

(defn content [web3 registry ens-name cb]
(ens/resolver web3
registry
ens-name
(fn [address]
(if (and address (not= address default-address))
(.content (.at (.contract (.-eth web3) resolver-abi) address)
(ens/namehash ens-name) #(cb %2))
(cb nil)))))

0 comments on commit c86fbb3

Please sign in to comment.