From 794313dbee85cc09a2f2ae9a6c7cb752f73cfc21 Mon Sep 17 00:00:00 2001 From: Igor Mandrigin Date: Fri, 21 Dec 2018 13:34:11 +0100 Subject: [PATCH] Revert "[Fixes #7052 and #7037] Improved extensions ethereum support" This reverts commit 6a8c9bf14f0f7bfcacdea45b1bad83a0da6a6550. Signed-off-by: Igor Mandrigin --- deps.edn | 2 +- project.clj | 2 +- src/status_im/chat/commands/core.cljs | 30 +- src/status_im/extensions/core.cljs | 240 ++++++------- src/status_im/extensions/ethereum.cljs | 331 +++++++++--------- src/status_im/extensions/registry.cljs | 5 +- .../ui/screens/wallet/settings/views.cljs | 21 +- src/status_im/utils/ethereum/abi_spec.cljs | 12 +- 8 files changed, 302 insertions(+), 341 deletions(-) diff --git a/deps.edn b/deps.edn index e314e1fa0e..c27e196821 100644 --- a/deps.edn +++ b/deps.edn @@ -11,7 +11,7 @@ com.taoensso/timbre {:mvn/version "4.10.0"} hickory {:mvn/version "0.7.1"} com.cognitect/transit-cljs {:mvn/version "0.8.248"} - status-im/pluto {:mvn/version "iteration-4-6"} + status-im/pluto {:mvn/version "iteration-4-5"} mvxcvi/alphabase {:mvn/version "1.0.0"} rasom/cljs-react-navigation {:mvn/version "0.1.4"}} diff --git a/project.clj b/project.clj index ac6220ec20..1de59c1f50 100644 --- a/project.clj +++ b/project.clj @@ -11,7 +11,7 @@ [com.taoensso/timbre "4.10.0"] [hickory "0.7.1"] [com.cognitect/transit-cljs "0.8.248"] - [status-im/pluto "iteration-4-6"] + [status-im/pluto "iteration-4-5"] [mvxcvi/alphabase "1.0.0"] [rasom/cljs-react-navigation "0.1.4"]] :plugins [[lein-cljsbuild "1.1.7"] diff --git a/src/status_im/chat/commands/core.cljs b/src/status_im/chat/commands/core.cljs index d510086493..95c82fefdc 100644 --- a/src/status_im/chat/commands/core.cljs +++ b/src/status_im/chat/commands/core.cljs @@ -114,17 +114,17 @@ (def command-hook "Hook for extensions" {:properties - {:description? :string - :scope #{:personal-chats :public-chats :group-chats} - :short-preview? :view - :preview? :view - :on-send? :event - :on-receive? :event - :on-send-sync? :event - :parameters? [{:id :keyword - :type {:one-of #{:text :phone :password :number}} - :placeholder :string - :suggestions? :view}]} + {:description? :string + :scope #{:personal-chats :public-chats} + :short-preview :view + :preview :view + :on-send? :event + :on-receive? :event + :on-send-sync? :event + :parameters? [{:id :keyword + :type {:one-of #{:text :phone :password :number}} + :placeholder :string + :suggestions? :view}]} :hook (reify hooks/Hook (hook-in [_ id {extension-id :id} {:keys [description scope parameters preview short-preview @@ -138,8 +138,8 @@ (validate [_ _ _]) (on-send [_ command-message _] (when on-send {:dispatch (on-send command-message)})) (on-receive [_ command-message _] (when on-receive {:dispatch (on-receive command-message)})) - (short-preview [_ props] (when short-preview (short-preview props))) - (preview [_ props] (when preview (preview props))) + (short-preview [_ props] (short-preview props)) + (preview [_ props] (preview props)) protocol/Yielding (yield-control [_ props _] {:dispatch (on-send-sync props)}) protocol/Extension @@ -152,8 +152,8 @@ (validate [_ _ _]) (on-send [_ command-message _] (when on-send {:dispatch (on-send command-message)})) (on-receive [_ command-message _] (when on-receive {:dispatch (on-receive command-message)})) - (short-preview [_ props] (when short-preview (short-preview props))) - (preview [_ props] (when preview (preview props))) + (short-preview [_ props] (short-preview props)) + (preview [_ props] (preview props)) protocol/Extension (extension-id [_] extension-id)))] (load-commands cofx [new-command]))) diff --git a/src/status_im/extensions/core.cljs b/src/status_im/extensions/core.cljs index 55475eebae..d5cc22e7bb 100644 --- a/src/status_im/extensions/core.cljs +++ b/src/status_im/extensions/core.cljs @@ -8,7 +8,6 @@ [status-im.chat.commands.impl.transactions :as transactions] [status-im.ui.components.button.view :as button] [status-im.ui.components.checkbox.view :as checkbox] - [status-im.ui.components.icons.vector-icons :as icons] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] [status-im.ui.screens.wallet.settings.views :as settings] @@ -22,7 +21,8 @@ status-im.extensions.ethereum [status-im.utils.ethereum.tokens :as tokens] [status-im.utils.ethereum.core :as ethereum] - [status-im.chat.commands.sending :as commands-sending])) + [status-im.chat.commands.sending :as commands-sending] + [status-im.ui.components.icons.vector-icons :as icons])) (re-frame/reg-fx ::identity-event @@ -90,7 +90,7 @@ :<- [:balance] (fn [[all-tokens network balance] [_ _ {token :token}]] (let [{:keys [decimals]} (get-token-for network all-tokens token) - value (or (get balance (keyword token)) (money/bignumber 0))] + value (get balance (keyword token))] {:value (money/token->unit value decimals) :value-in-wei value}))) @@ -98,26 +98,20 @@ :extensions.wallet/token :<- [:wallet/all-tokens] :<- [:network] - (fn [[all-tokens network] [_ _ {token :token amount :amount amount-in-wei :amount-in-wei}]] + (fn [[all-tokens network] [_ _ {token :token amount :amount}]] (let [{:keys [decimals] :as m} (get-token-for network all-tokens token)] (merge m - (when amount {:amount-in-wei (money/unit->token amount decimals)}) - (when amount-in-wei {:amount (money/token->unit amount-in-wei decimals)}))))) - -(defn normalize-token [m] - (update m :symbol name)) + (when amount {:amount (money/unit->token amount decimals)}))))) (re-frame/reg-sub :extensions.wallet/tokens :<- [:wallet/all-tokens] - :<- [:wallet/visible-tokens-symbols] :<- [:network] - (fn [[all-tokens visible-tokens-symbols network] [_ _ {filter-vector :filter visible :visible}]] - (let [tokens (map normalize-token (filter #(and (not (:nft? %)) (if visible (contains? visible-tokens-symbols (:symbol %)) true)) - (tokens/sorted-tokens-for all-tokens (ethereum/network->chain-keyword network))))] - (if filter-vector - (filter #((set filter-vector) (:symbol %)) tokens) - tokens)))) + (fn [[all-tokens network] [_ _ {filter-vector :filter}]] + (let [tokens (tokens/sorted-tokens-for all-tokens (ethereum/network->chain-keyword network))] + (if (= :all (first filter-vector)) + tokens + (filter #((set filter-vector) (:symbol %)) tokens))))) (re-frame/reg-sub :store/get @@ -366,57 +360,43 @@ (defn picker [{:keys [style on-change selected enabled data]}] [react/picker {:style style :on-change #(re-frame/dispatch (on-change {:value %})) :selected selected :enabled enabled :data data}]) -(defn- wrap-text-child [o] - (if (ifn? o) o (str o))) - (defn text [o & children] (if (map? o) - (into [react/text o] (map wrap-text-child children)) - (into [react/text {} o] (map wrap-text-child children)))) + [react/text o children] + (into [react/text {} o] children))) (defn- wrap-view-child [child] (if (vector? child) child [text {} child])) -(defn abstract-view [type o & children] - (if (map? o) - (into [type o] (map wrap-view-child children)) - (into [type {} (wrap-view-child o)] (map wrap-view-child children)))) - (defn view [o & children] - (apply abstract-view react/view o children)) + (if (map? o) + (into [react/view o] (map wrap-view-child children)) + (into [react/view {} (wrap-view-child o)] (map wrap-view-child children)))) -(defn scroll-view [o & children] - (apply abstract-view react/scroll-view o children)) - -(defn keyboard-avoiding-view [o & children] - (apply abstract-view react/keyboard-avoiding-view o children)) - -(defn icon [{:keys [key] :as o}] - [icons/icon key o]) +(defn icon [o] + [icons/icon (:key o) o]) (def capacities - {:components {'view {:value view} - 'scroll-view {:value scroll-view} - 'keyboard-avoiding-view {:value react/keyboard-avoiding-view} - 'text {:value text} - 'touchable-opacity {:value touchable-opacity :properties {:on-press :event}} - 'icon {:value icon :properties {:key :keyword :color :any}} - 'image {:value image :properties {:uri :string :source :string}} - 'input {:value input :properties {:on-change :event :placeholder :string :keyboard-type :keyword :change-delay? :number :placeholder-text-color :any}} - 'button {:value button :properties {:enabled :boolean :disabled :boolean :on-click :event}} - 'link {:value link :properties {:uri :string}} - 'list {:value list :properties {:data :vector :item-view :view :key? :keyword}} - 'checkbox {:value checkbox :properties {:on-change :event :checked :boolean}} - 'activity-indicator {:value activity-indicator :properties {:animating :boolean :color :string :size :keyword :hides-when-stopped :boolean}} - 'picker {:value picker :properties {:on-change :event :selected :string :enabled :boolean :data :vector}} - 'nft-token-viewer {:value transactions/nft-token :properties {:token :string}} - 'transaction-status {:value transactions/transaction-status :properties {:outgoing :string :tx-hash :string}}} + {:components {'view {:value view} + 'text {:value text} + 'touchable-opacity {:value touchable-opacity :properties {:on-press :event}} + 'icon {:value icon :properties {:key :keyword :color :any}} + 'image {:value image :properties {:uri :string :source :numeric}} + 'input {:value input :properties {:on-change :event :change-delay :number :placeholder :string :keyboard-type :keyword :placeholder-text-color :any}} + 'button {:value button :properties {:enabled :boolean :disabled :boolean :on-click :event}} + 'link {:value link :properties {:uri :string}} + 'list {:value list :properties {:data :vector :item-view :view :key? :keyword}} + 'checkbox {:value checkbox :properties {:on-change :event :checked :boolean}} + 'activity-indicator {:value activity-indicator :properties {:animating :boolean :color :string :size :keyword :hides-when-stopped :boolean}} + 'picker {:value picker :properties {:on-change :event :selected :string :enabled :boolean :data :vector}} + 'nft-token-viewer {:value transactions/nft-token :properties {:token :string}} + 'transaction-status {:value transactions/transaction-status :properties {:outgoing :string :tx-hash :string}}} :queries {'identity {:value :extensions/identity :arguments {:value :map}} 'store/get {:value :store/get :arguments {:key :string}} 'wallet/collectibles {:value :get-collectible-token :arguments {:token :string :symbol :string}} 'wallet/balance {:value :extensions.wallet/balance :arguments {:token :string}} - 'wallet/token {:value :extensions.wallet/token :arguments {:token :string :amount? :number :amount-in-wei? :number}} - 'wallet/tokens {:value :extensions.wallet/tokens :arguments {:filter? :vector :visible? :boolean}}} + 'wallet/token {:value :extensions.wallet/token :arguments {:token :string :amount? :numeric}} + 'wallet/tokens {:value :extensions.wallet/tokens :arguments {:filter :vector}}} :events {'identity {:permissions [:read] :value :extensions/identity-event @@ -457,7 +437,7 @@ {:permissions [:read] :value :extensions/arithmetic :arguments {:values :vector - :operation {:one-of #{:plus :minus :times :divide}} + :operation :keyword :on-result :event}} 'schedule/start {:permissions [:read] @@ -525,158 +505,140 @@ 'ethereum/transaction-receipt {:permissions [:read] :value :extensions/ethereum-transaction-receipt - :arguments {:value :string - :on-success :event - :on-failure? :event}} + :arguments {:value :string + :on-result :event}} 'ethereum/sign {:permissions [:read] :value :extensions/ethereum-sign - :arguments {:message? :string - :data? :string - :on-success :event - :on-failure? :event}} + :arguments {:message? :string + :data? :string + :on-result :event}} 'ethereum/send-transaction {:permissions [:read] :value :extensions/ethereum-send-transaction - :arguments {:to :string - :gas? :string - :gas-price? :string - :value? :string - :method? :string - :params? :vector - :nonce? :string - :on-success :event - :on-failure? :event}} + :arguments {:to :string + :gas? :string + :gas-price? :string + :value? :string + :method? :string + :params? :vector + :nonce? :string + :on-result :event}} 'ethereum/logs {:permissions [:read] :value :extensions/ethereum-logs - :arguments {:from? :string - :to? :string - :address? :vector - :topics? :vector - :block-hash? :string - :on-success :event - :on-failure? :event}} + :arguments {:fromBlock? :string + :toBlock? :string + :address? :vector + :topics? :vector + :blockhash? :string + :on-result :event}} + 'ethereum/resolve-ens + {:permissions [:read] + :value :extensions/ethereum-resolve-ens + :arguments {:name :string + :on-result :event}} 'ethereum/create-filter {:permissions [:read] :value :extensions/ethereum-create-filter - :arguments {:type {:one-of #{:filter :block :pending-transaction}} - :from? :string - :to? :string - :address? :vector - :topics? :vector + :arguments {:filter-type :string + :from? :string + :to? :string + :address? :vector + :topics? :vector :block-hash? :string - :on-success :event - :on-failure? :event}} + :on-result :event}} 'ethereum/logs-changes {:permissions [:read] :value :extensions/ethereum-logs-changes - :arguments {:id :string}} + :arguments {:id :string}} 'ethereum/cancel-filter {:permissions [:read] :value :extensions/ethereum-cancel-filter :arguments {:id :string}} - 'ethereum.ens/resolve - {:permissions [:read] - :value :extensions/ethereum-resolve-ens - :arguments {:name :string - :on-success :event - :on-failure? :event}} 'ethereum.erc20/total-supply {:permissions [:read] :value :extensions/ethereum-erc20-total-supply :arguments {:contract :string - :on-success :event - :on-failure? :event}} + :on-result :event}} 'ethereum.erc20/balance-of {:permissions [:read] :value :extensions/ethereum-erc20-balance-of :arguments {:contract :string :token-owner :string - :on-success :event - :on-failure? :event}} + :on-result :event}} 'ethereum.erc20/transfer {:permissions [:read] :value :extensions/ethereum-erc20-transfer - :arguments {:contract :string - :to :string - :value :number - :on-success :event - :on-failure? :event}} + :arguments {:contract :string + :to :string + :value :number + :on-result :event}} 'ethereum.erc20/transfer-from {:permissions [:read] :value :extensions/ethereum-erc20-transfer-from - :arguments {:contract :string - :from :string - :to :string - :value :number - :on-success :event - :on-failure? :event}} + :arguments {:contract :string + :from :string + :to :string + :value :number + :on-result :event}} 'ethereum.erc20/approve {:permissions [:read] :value :extensions/ethereum-erc20-approve :arguments {:contract :string :spender :string :value :number - :on-success :event - :on-failure? :event}} + :on-result :event}} 'ethereum.erc20/allowance {:permissions [:read] :value :extensions/ethereum-erc20-allowance :arguments {:contract :string :token-owner :string :spender :string - :on-success :event - :on-failure? :event}} + :on-result :event}} 'ethereum.erc721/owner-of {:permissions [:read] :value :extensions/ethereum-erc721-owner-of :arguments {:contract :string :token-id :string - :on-success :event - :on-failure? :event}} + :on-result :event}} 'ethereum.erc721/is-approved-for-all {:permissions [:read] :value :extensions/ethereum-erc721-is-approved-for-all - :arguments {:contract :string - :owner :string - :operator :string - :on-success :event - :on-failure? :event}} + :arguments {:contract :string + :owner :string + :operator :string + :on-result :event}} 'ethereum.erc721/get-approved {:permissions [:read] :value :extensions/ethereum-erc721-get-approved - :arguments {:contract :string - :token-id :string - :on-success :event - :on-failure? :event}} + :arguments {:contract :string + :token-id :string + :on-result :event}} 'ethereum.erc721/set-approval-for-all {:permissions [:read] :value :extensions/ethereum-erc721-set-approval-for-all - :arguments {:contract :string - :operator :string - :approved :boolean - :on-success :event - :on-failure? :event}} + :arguments {:contract :string + :operator :string + :approved :boolean + :on-result :event}} 'ethereum.erc721/safe-transfer-from {:permissions [:read] :value :extensions/ethereum-erc721-safe-transfer-from - :arguments {:contract :string - :from :string - :to :string - :token-id :string - :data? :string - :on-success :event - :on-failure? :event}} + :arguments {:contract :string + :from :string + :to :string + :token-id :string + :data? :string + :on-result :event}} 'ethereum/call {:permissions [:read] :value :extensions/ethereum-call - :arguments {:to :string - :method :string - :params? :vector - :outputs? :vector - :on-success :event - :on-failure? :event}}} + :arguments {:to :string + :method :string + :params? :vector + :outputs? :vector + :on-result :event}}} :hooks {:chat.command commands/command-hook :wallet.settings settings/hook}}) diff --git a/src/status_im/extensions/ethereum.cljs b/src/status_im/extensions/ethereum.cljs index aa029a5818..082e008fa7 100644 --- a/src/status_im/extensions/ethereum.cljs +++ b/src/status_im/extensions/ethereum.cljs @@ -17,20 +17,23 @@ [status-im.native-module.core :as status])) (handlers/register-handler-fx - :extensions/wallet-ui-on-success - (fn [cofx [_ on-success _ result _]] + :extensions/wallet-ui-on-result + (fn [cofx [_ on-result id result method]] (fx/merge cofx - {:dispatch (on-success {:value result})} - (navigation/navigate-back)))) + (when on-result + {:dispatch (on-result {:error nil :result result})}) + (cond + (= method constants/web3-send-transaction) (navigation/navigate-to-clean :wallet-transaction-sent nil) + (= method constants/web3-personal-sign) (navigation/navigate-back))))) (handlers/register-handler-fx - :extensions/wallet-ui-on-failure - (fn [_ [_ on-failure message]] - (when on-failure {:dispatch (on-failure {:value message})}))) + :extensions/wallet-ui-on-error + (fn [{db :db} [_ on-result message]] + (when on-result {:dispatch (on-result {:error message :result nil})}))) (defn- wrap-with-resolution [db arguments address-keyword f] - "function responsible to resolve ens taken from argument - and call the specified function with resolved address" + "funtction responsible to resolve ens taken from argument + and call the specified function with resolved address" (let [address (get arguments address-keyword) first-address (if (vector? address) ;; currently we only support one ens for address (first address) @@ -43,7 +46,8 @@ (ens/get-addr web3 registry first-address #(f db (assoc arguments address-keyword %)))) (f db arguments)))) -(defn prepare-extension-transaction [params contacts on-success on-failure] +;; EXTENSION TRANSACTION -> SEND TRANSACTION +(defn prepare-extension-transaction [params contacts on-result] (let [{:keys [to value data gas gasPrice nonce]} params contact (get contacts (hex/normalize-hex to))] (cond-> {:id "extension-id" @@ -62,15 +66,15 @@ :gas-price (when gasPrice (money/bignumber gasPrice)) :data data - :on-result [:extensions/wallet-ui-on-success on-success] - :on-error [:extensions/wallet-ui-on-failure on-failure]} + :on-result [:extensions/wallet-ui-on-result on-result] + :on-error [:extensions/wallet-ui-on-error on-result]} nonce (assoc :nonce nonce)))) -(defn- execute-send-transaction [db {:keys [method params on-success on-failure] :as arguments}] +(defn- execute-send-transaction [db {:keys [method params on-result] :as arguments}] (let [tx-object (assoc (select-keys arguments [:to :gas :gas-price :value :nonce]) :data (when (and method params) (abi-spec/encode method params))) - transaction (prepare-extension-transaction tx-object (:contacts/contacts db) on-success on-failure)] + transaction (prepare-extension-transaction tx-object (:contacts/contacts db) on-result)] (models.wallet/open-modal-wallet-for-transaction db transaction tx-object))) (handlers/register-handler-fx @@ -78,173 +82,143 @@ (fn [{db :db} [_ _ arguments]] (wrap-with-resolution db arguments :to execute-send-transaction))) -(defn- rpc-args [method params] - {:jsonrpc "2.0" - :method method - :params params}) - -(defn- rpc-dispatch [error result f on-success on-failure] - (when result - (re-frame/dispatch (on-success {:result (f result)}))) - (when (and error on-failure) - (re-frame/dispatch (on-failure {:result error})))) - -(defn- rpc-handler [o f on-success on-failure] - (let [{:keys [error result]} (types/json->clj o)] - (rpc-dispatch error result f on-success on-failure))) - -(defn- rpc-call [method params f {:keys [on-success on-failure]}] - (let [payload (types/clj->json (rpc-args method params))] - (status/call-private-rpc payload #(rpc-handler % f on-success on-failure)))) - -(defn parse-call-result [o outputs] - (let [result (get (js->clj o) "result")] - (cond - (= "0x" result) nil - (and outputs result) - (abi-spec/decode (string/replace result #"^0x" "") outputs) - :else result))) - -(defn- execute-ethcall [_ {:keys [to method params outputs on-success on-failure]}] +(defn- execute-ethcall [_ {:keys [to method params outputs on-result]}] (let [tx-object {:to to :data (when method (abi-spec/encode method params))}] - {:browser/call-rpc [(rpc-args "eth_call" [tx-object "latest"]) - #(rpc-dispatch %1 %2 (fn [o] (parse-call-result o outputs)) on-success on-failure)]})) + {:browser/call-rpc [{"jsonrpc" "2.0" + "method" "eth_call" + "params" [tx-object "latest"]} + #(let [result-str (when %2 + (get (js->clj %2) "result")) + result (cond + (= "0x" result-str) nil + (and outputs result-str) + (abi-spec/decode (string/replace result-str #"0x" "") outputs) + :else result-str)] + (re-frame/dispatch (on-result (merge {:result result} (when %1 {:error %1})))))]})) (handlers/register-handler-fx :extensions/ethereum-call - (fn [{db :db} [_ _ arguments]] + (fn [{db :db} [_ _ {:keys [to] :as arguments}]] (wrap-with-resolution db arguments :to execute-ethcall))) (handlers/register-handler-fx :extensions/ethereum-erc20-total-supply - (fn [{db :db} [_ _ {:keys [contract on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract on-result]}]] (let [json-rpc-args {:to contract - :method "totalSupply()" - :outputs ["uint256"] - :on-success on-success - :on-failure on-failure}] + :method "totalSupply()" + :outputs ["uint256"] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (handlers/register-handler-fx :extensions/ethereum-erc20-balance-of - (fn [{db :db} [_ _ {:keys [contract token-owner on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract token-owner on-result]}]] (let [json-rpc-args {:to contract - :method "balanceOf(address)" - :params [token-owner] - :outputs ["uint256"] - :on-success on-success - :on-failure on-failure}] + :method "balanceOf(address)" + :params [token-owner] + :outputs ["uint256"] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (handlers/register-handler-fx :extensions/ethereum-erc20-allowance - (fn [{db :db} [_ _ {:keys [contract token-owner spender on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract token-owner spender on-result]}]] (let [json-rpc-args {:to contract - :method "allowance(address,address)" - :params [token-owner spender] - :outputs ["uint256"] - :on-success on-success - :on-failure on-failure}] + :method "allowance(address,address)" + :params [token-owner spender] + :outputs ["uint256"] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (handlers/register-handler-fx :extensions/ethereum-erc20-transfer - (fn [{db :db} [_ _ {:keys [contract to value on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract to value on-result]}]] (let [json-rpc-args {:to contract - :method "transfer(address,uint256)" - :params [to value] - :on-success on-success - :on-failure on-failure}] + :method "transfer(address,uint256)" + :params [to value] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (handlers/register-handler-fx :extensions/ethereum-erc20-transfer-from - (fn [{db :db} [_ _ {:keys [contract from to value on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract from to value on-result]}]] (let [json-rpc-args {:to contract - :method "transferFrom(address,address,uint256)" - :params [from to value] - :on-success on-success - :on-failure on-failure}] + :method "transferFrom(address,address,uint256)" + :params [from to value] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (handlers/register-handler-fx :extensions/ethereum-erc20-approve - (fn [{db :db} [_ _ {:keys [contract spender value on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract spender value on-result]}]] (let [json-rpc-args {:to contract - :method "approve(address,uint256)" - :params [spender value] - :on-success on-success - :on-failure on-failure}] + :method "approve(address,uint256)" + :params [spender value] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (handlers/register-handler-fx :extensions/ethereum-erc721-owner-of - (fn [{db :db} [_ _ {:keys [contract token-id on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract token-id on-result]}]] (let [json-rpc-args {:to contract - :method "ownerOf(uint256)" - :params [token-id] - :outputs ["address"] - :on-success on-success - :on-failure on-failure}] + :method "ownerOf(uint256)" + :params [token-id] + :outputs ["address"] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (handlers/register-handler-fx :extensions/ethereum-erc721-is-approved-for-all - (fn [{db :db} [_ _ {:keys [contract owner operator on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract owner operator on-result]}]] (let [json-rpc-args {:to contract - :method "isApprovedForAll(address,address)" - :params [owner operator] - :outputs ["bool"] - :on-success on-success - :on-failure on-failure}] + :method "isApprovedForAll(address,address)" + :params [owner operator] + :outputs ["bool"] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (handlers/register-handler-fx :extensions/ethereum-erc721-get-approved - (fn [{db :db} [_ _ {:keys [contract token-id on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract token-id on-result]}]] (let [json-rpc-args {:to contract - :method "getApproved(uint256)" - :params [token-id] - :outputs ["address"] - :on-success on-success - :on-failure on-failure}] + :method "getApproved(uint256)" + :params [token-id] + :outputs ["address"] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (handlers/register-handler-fx :extensions/ethereum-erc721-set-approval-for-all - (fn [{db :db} [_ _ {:keys [contract to approved on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract to approved on-result]}]] (let [json-rpc-args {:to contract - :method "setApprovalForAll(address,bool)" - :params [to approved] - :on-success on-success - :on-failure on-failure}] + :method "setApprovalForAll(address,bool)" + :params [to approved] + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (handlers/register-handler-fx :extensions/ethereum-erc721-safe-transfer-from - (fn [{db :db} [_ _ {:keys [contract from to token-id data on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [contract from to token-id data on-result]}]] (let [json-rpc-args {:to contract - :method (if data - "safeTransferFrom(address,address,uint256,bytes)" - "safeTransferFrom(address,address,uint256)") - :params (if data - [from to token-id data] - [from to token-id]) - :on-success on-success - :on-failure on-failure}] + :method (if data + "safeTransferFrom(address,address,uint256,bytes)" + "safeTransferFrom(address,address,uint256)") + :params (if data + [from to token-id data] + [from to token-id]) + :on-result on-result}] (wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (defn- parse-log [{:keys [address transactionHash blockHash transactionIndex topics blockNumber logIndex removed data]}] - (merge {:data data - :topics topics - :address address - :removed removed} - ;; TODO parse data and topics, filter useless solidity first topic, aggregate as events ? - (when logIndex {:log-index (abi-spec/hex-to-number logIndex)}) - (when transactionIndex {:transaction-index (abi-spec/hex-to-number transactionIndex)}) - (when transactionHash {:transaction-hash transactionHash}) - (when blockHash {:block-hash blockHash}) - (when blockNumber {:block-number (abi-spec/hex-to-number blockNumber)}))) + {:address address + :transaction-hash transactionHash + :blockHash blockHash + :transaction-index (abi-spec/hex-to-number transactionIndex) + :topics topics ;; TODO parse topics + :block-number (abi-spec/hex-to-number blockNumber) + :log-index (abi-spec/hex-to-number logIndex) + :removed removed + :data data}) (defn- parse-receipt [m] (when m @@ -264,8 +238,16 @@ (handlers/register-handler-fx :extensions/ethereum-transaction-receipt - (fn [_ [_ _ {:keys [value] :as m}]] - (rpc-call constants/web3-transaction-receipt [value] parse-receipt m))) + (fn [_ [_ _ {:keys [value on-result]}]] + (let [args {:jsonrpc "2.0" + :method constants/web3-transaction-receipt + :params [value]} + payload (types/clj->json args)] + (status/call-private-rpc payload #(let [{:keys [error result]} (types/json->clj %1) + response (merge {:result (parse-receipt result)} (when error {:error error}))] + (re-frame/dispatch (on-result response))))))) + +;; eth_getLogs implementation (defn- event-topic-enc [event params] (let [eventid (str event "(" (string/join "," params) ")")] @@ -282,16 +264,17 @@ :else :bytes)) (defn- values-topic-enc [type values] - (mapv #(str "0x" (abi-spec/enc {:type (types-mapping type) :value %})) values)) + (let [mapped-type (types-mapping type)] + (mapv #(str "0x" (abi-spec/enc {:type mapped-type :value %})) values))) -(defn- generate-topic [t] +(defn- parse-topic [t] (cond (or (nil? t) (string? t)) t ;; nil topic ;; immediate topic (extension encode topic by its own) ;; vector of immediate topics - (vector? t) (mapv generate-topic t) ;; descend in vector elements + (vector? t) (mapv parse-topic t) ;; descend in vector elements (map? t) ;; simplified topic interface, we need to encode (let [{:keys [event params type values]} t] (cond - (some? event) (event-topic-enc event params);; event params topic {:event "Transfer" :params ["uint"]} + (some? event) (event-topic-enc event params);; event params topic (some? type) (values-topic-enc type values) ;; indexed values topic :else nil)) ;; error :else nil)) @@ -302,13 +285,19 @@ (re-matches #"^[0-9]+$" block) (str "0x" (abi-spec/number-to-hex block)) :else block)) -(defn- execute-get-logs [_ {:keys [from to address topics block-hash] :as m}] - (let [params [{:from (ensure-hex-bn from) - :to (ensure-hex-bn to) - :address address - :topics (generate-topic topics) - :blockhash block-hash}]] - (rpc-call constants/web3-get-logs params #(map parse-log %) m))) +(defn- execute-get-logs [_ {:keys [fromBlock toBlock address topics blockhash on-result]}] + (let [parsed-topics (mapv parse-topic topics) + args {:jsonrpc "2.0" + :method constants/web3-get-logs + :params [{:fromBlock (ensure-hex-bn fromBlock) + :toBlock (ensure-hex-bn toBlock) + :address address + :topics parsed-topics + :blockhash blockhash}]} + payload (types/clj->json args)] + (status/call-private-rpc payload #(let [{:keys [error result]} (types/json->clj %1) + response (merge {:result result} (when error {:error error}))] + (re-frame/dispatch (on-result response)))))) (handlers/register-handler-fx :extensions/ethereum-logs @@ -317,59 +306,71 @@ (handlers/register-handler-fx :extensions/ethereum-resolve-ens - (fn [{db :db} [_ _ {:keys [name on-success on-failure]}]] + (fn [{db :db} [_ _ {:keys [name on-result]}]] (if (ens/is-valid-eth-name? name) (let [{:keys [web3 network]} db network-info (get-in db [:account/account :networks network]) chain (ethereum/network->chain-keyword network-info) registry (get ens/ens-registries chain)] - (ens/get-addr web3 registry name #(re-frame/dispatch (on-success {:value %})))) - (when on-failure - (re-frame/dispatch (on-failure {:value (str "'" name "' is not a valid name")})))))) + (ens/get-addr web3 registry name #(re-frame/dispatch (on-result {:result %})))) + (re-frame/dispatch (on-result {:error (str "'" name "' is not a valid name")}))))) ;; EXTENSION SIGN -> SIGN MESSAGE (handlers/register-handler-fx :extensions/ethereum-sign - (fn [{db :db :as cofx} [_ _ {:keys [message data id on-success on-failure]}]] - (if (and message data) - (when on-failure - {:dispatch (on-failure {:error "only one of :message and :data can be used"})}) + (fn [{db :db :as cofx} [_ _ {:keys [message data id on-result]}]] + (if (= (nil? message) (nil? data)) + {:dispatch (on-result {:error "only one of :message and :data can be used"})} (fx/merge cofx {:db (assoc-in db [:wallet :send-transaction] {:id id - :from (ethereum/normalized-address (get-in db [:account/account :address])) + :from (ethereum/normalized-address (get-in db [:account/account :address])) :data (or data (str "0x" (abi-spec/from-utf8 message))) - :on-result [:extensions/wallet-ui-on-success on-success] - :on-error [:extensions/wallet-ui-on-failure on-failure] + :on-result [:extensions/wallet-ui-on-result on-result] + :on-error [:extensions/wallet-ui-on-error on-result] :method constants/web3-personal-sign})} (navigation/navigate-to-cofx :wallet-sign-message-modal nil))))) +;; poll logs implementation (handlers/register-handler-fx :extensions/ethereum-logs-changes - (fn [_ [_ _ {:keys [id] :as m}]] - (rpc-call constants/web3-get-filter-changes [(abi-spec/number-to-hex id)] #(map parse-log %) m))) - + (fn [_ [_ _ {:keys [filterId on-result]}]] + (let [args {:jsonrpc "2.0" + :method constants/web3-get-filter-changes + :params [filterId]} + payload (types/clj->json args)] + (status/call-private-rpc payload #(let [{:keys [error result]} (types/json->clj %1) + response (merge {:result result} (when error {:error error}))] + (mapv (fn [one-result] + (re-frame/dispatch (on-result one-result))) result)))))) (handlers/register-handler-fx :extensions/ethereum-cancel-filter - (fn [_ [_ _ {:keys [id] :as m}]] - (rpc-call constants/web3-uninstall-filter [(abi-spec/number-to-hex id)] #(abi-spec/hex-to-value % "bool") m))) - -(defn create-filter-method [type] - (case type - :filter constants/web3-new-filter - :block constants/web3-new-block-filter - :pending-transaction constants/web3-new-pending-transaction-filter)) - -(defn create-filter-arguments [type {:keys [from to address block-hash topics]}] - (case type - :filter - [{:fromBlock (ensure-hex-bn from) - :toBlock (ensure-hex-bn to) - :address address - :topics (mapv generate-topic topics) - :blockhash block-hash}])) + (fn [_ [_ _ {:keys [filterId on-result]}]] + (let [args {:jsonrpc "2.0" + :method constants/web3-uninstall-filter + :params [filterId]} + payload (types/clj->json args)] + (status/call-private-rpc payload #(let [{:keys [error result]} (types/json->clj %1) + response (merge {:result result} (when error {:error error}))] + (re-frame/dispatch (on-result response))))))) (handlers/register-handler-fx :extensions/ethereum-create-filter - (fn [_ [_ _ {:keys [type] :as m}]] - (rpc-call (create-filter-method type) (create-filter-arguments type m) abi-spec/hex-to-number m))) + (fn [_ [_ _ {:keys [filter-type from to address topics block-hash on-result]}]] + (let [parsed-topics (mapv parse-topic topics) + args (case filter-type + "filter" {:jsonrpc "2.0" + :method constants/web3-new-filter + :params [{:fromBlock (ensure-hex-bn from) + :toBlock (ensure-hex-bn to) + :address address + :topics parsed-topics + :blockhash block-hash}]} + "block" {:jsonrpc "2.0" + :method constants/web3-new-block-filter} + "pendingTransaction" {:jsonrpc "2.0" + :method constants/web3-new-pending-transaction-filter}) + payload (types/clj->json args)] + (status/call-private-rpc payload #(let [{:keys [error result]} (types/json->clj %1) + response (merge {:result result} (when error {:error error}))] + (re-frame/dispatch (on-result response))))))) diff --git a/src/status_im/extensions/registry.cljs b/src/status_im/extensions/registry.cljs index 46678071b6..95ec0733d5 100644 --- a/src/status_im/extensions/registry.cljs +++ b/src/status_im/extensions/registry.cljs @@ -15,9 +15,8 @@ hooks (get-in account [:extensions extension-id :hooks])] (apply fx/merge cofx (mapcat (fn [[_ extension-hooks]] - (map (fn [[hook-id {parsed :parsed {hook :hook} :hook-ref}]] - (when hook - (partial hook-fn hook hook-id {:id extension-id} parsed))) + (map (fn [[hook-id {:keys [hook-ref parsed]}]] + (partial hook-fn (:hook hook-ref) hook-id {:id extension-id} parsed)) extension-hooks)) hooks)))) diff --git a/src/status_im/ui/screens/wallet/settings/views.cljs b/src/status_im/ui/screens/wallet/settings/views.cljs index 99d1ec802b..76ef4d2688 100644 --- a/src/status_im/ui/screens/wallet/settings/views.cljs +++ b/src/status_im/ui/screens/wallet/settings/views.cljs @@ -18,12 +18,13 @@ "Hook for extensions" {:properties {:label :string - :view :view} + :view :view + :on-click? :event} :hook (reify hooks/Hook - (hook-in [_ id _ {:keys [label view _]} {:keys [db]}] - {:db (assoc-in db [:wallet :settings id] {:label label :view view})}) - (unhook [_ id _ _ {:keys [db]}] + (hook-in [_ id env {:keys [label view on-click]} {:keys [db]}] + {:db (assoc-in db [:wallet :settings id] {:label label :view view :on-click on-click})}) + (unhook [_ id env _ {:keys [db]}] {:db (update-in db [:wallet :settings] dissoc id)}))}) (defn- render-token [{:keys [symbol name icon]} visible-tokens] @@ -56,8 +57,7 @@ :render-fn #(render-token % visible-tokens)}]]])) (defview settings-hook [] - (letsubs [{:keys [label view]} [:get-screen-params :wallet-settings-hook] - {address :address} [:account/account]] + (letsubs [{:keys [label view]} [:get-screen-params :wallet-settings-hook]] [react/view {:style {:flex 1 :background-color colors/white}} [status-bar/status-bar {:type :modal-wallet}] [toolbar/toolbar {:style wallet.styles/toolbar} @@ -65,11 +65,14 @@ (re-frame/dispatch [:navigate-back])))] [toolbar/content-title {:color colors/white} label]] - [view {:address (ethereum/normalized-address address)}]])) + [view]])) -(defn- setting->action [{:keys [label] :as m}] +(defn- setting->action [{:keys [label on-click] :as m}] {:label label - :action #(re-frame/dispatch [:navigate-to :wallet-settings-hook m])}) + :action #(do + (when on-click + (re-frame/dispatch (on-click {}))) + (re-frame/dispatch [:navigate-to :wallet-settings-hook m]))}) (defview toolbar-view [] (letsubs [settings [:wallet/settings]] diff --git a/src/status_im/utils/ethereum/abi_spec.cljs b/src/status_im/utils/ethereum/abi_spec.cljs index a19ca3bb6e..ad5669572b 100644 --- a/src/status_im/utils/ethereum/abi_spec.cljs +++ b/src/status_im/utils/ethereum/abi_spec.cljs @@ -22,20 +22,16 @@ (.leftPad utils x (+ len to-pad))))) (defn to-two-complement [x] - (when x - (subs (.toTwosComplement utils x) 2))) + (subs (.toTwosComplement utils x) 2)) (defn from-utf8 [x] - (when x - (subs (.fromUtf8 utils x) 2))) + (subs (.fromUtf8 utils x) 2)) (defn bytes-to-hex [x] - (when x - (subs (.bytesToHex utils x) 2))) + (subs (.bytesToHex utils x) 2)) (defn number-to-hex [x] - (when x - (subs (.numberToHex utils x) 2))) + (subs (.numberToHex utils x) 2)) (defn hex-to-utf8 [x] (.hexToUtf8 utils (str "0x" x)))