diff --git a/src/status_im/constants.cljs b/src/status_im/constants.cljs index 00c8e3eb9c..379b0ad6a0 100644 --- a/src/status_im/constants.cljs +++ b/src/status_im/constants.cljs @@ -200,6 +200,7 @@ (def ^:const web3-send-transaction "eth_sendTransaction") (def ^:const web3-personal-sign "personal_sign") (def ^:const web3-get-logs "eth_getLogs") +(def ^:const web3-transaction-receipt "eth_getTransactionReceipt") (def ^:const event-transfer-hash (ethereum/sha3 "Transfer(address,address,uint256)")) diff --git a/src/status_im/extensions/core.cljs b/src/status_im/extensions/core.cljs index 85a14ab852..26ce2ef870 100644 --- a/src/status_im/extensions/core.cljs +++ b/src/status_im/extensions/core.cljs @@ -12,6 +12,7 @@ [status-im.ui.components.react :as react] [status-im.ui.screens.wallet.settings.views :as settings] [status-im.i18n :as i18n] + [status-im.utils.money :as money] [status-im.ui.components.colors :as colors] [status-im.ui.screens.navigation :as navigation] [status-im.utils.handlers :as handlers] @@ -21,6 +22,15 @@ [status-im.utils.ethereum.core :as ethereum] [status-im.chat.commands.sending :as commands-sending])) +(re-frame/reg-fx + ::identity-event + (fn [{:keys [cb]}] (re-frame/dispatch (cb {})))) + +(re-frame/reg-event-fx + :extensions/identity-event + (fn [_ [_ _ m]] + {::identity-event m})) + (re-frame/reg-fx ::alert (fn [value] (js/alert value))) @@ -39,11 +49,58 @@ (fn [_ [_ _ {:keys [value]}]] {::log value})) +(re-frame/reg-fx + ::schedule-start + (fn [{:keys [interval on-created on-result]}] + (let [id (js/setInterval #(re-frame/dispatch (on-result {})) interval)] + (re-frame/dispatch (on-created {:value id}))))) + +(handlers/register-handler-fx + :extensions/schedule-start + (fn [_ [_ _ m]] + {::schedule-start m})) + +(re-frame/reg-fx + ::schedule-cancel + (fn [{:keys [value]}] + (js/clearInterval value))) + +(handlers/register-handler-fx + :extensions/schedule-cancel + (fn [_ [_ _ m]] + {::schedule-cancel m})) + (re-frame/reg-sub :extensions/identity (fn [_ [_ _ {:keys [value]}]] value)) +(defn get-token-for [network all-tokens token] + (if (= token "ETH") + {:decimals 18 + :address "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"} + (tokens/token-for (ethereum/network->chain-keyword network) all-tokens token))) + +(re-frame/reg-sub + :extensions.wallet/balance + :<- [:wallet/all-tokens] + :<- [:network] + :<- [:balance] + (fn [[all-tokens network balance] [_ _ {token :token}]] + (let [{:keys [decimals]} (get-token-for network all-tokens token) + value (get balance (keyword token))] + {:value (money/token->unit value decimals) + :value-in-wei value}))) + +(re-frame/reg-sub + :extensions.wallet/token + :<- [:wallet/all-tokens] + :<- [:network] + (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 (money/unit->token amount decimals)}))))) + (re-frame/reg-sub :extensions.wallet/tokens :<- [:wallet/all-tokens] @@ -59,10 +116,25 @@ (fn [db [_ {id :id} {:keys [key]}]] (get-in db [:extensions/store id key]))) +(defn- empty-value? [o] + (cond + (seqable? o) (empty? o) + :else (nil? o))) + +(defn put-or-dissoc [db id key value] + (if (empty-value? value) + (update-in db [:extensions/store id] dissoc key) + (assoc-in db [:extensions/store id key] value))) + (handlers/register-handler-fx :store/put (fn [{:keys [db]} [_ {id :id} {:keys [key value]}]] - {:db (assoc-in db [:extensions/store id key] value)})) + {:db (put-or-dissoc db id key value)})) + +(handlers/register-handler-fx + :store/puts + (fn [{:keys [db]} [_ {id :id} {:keys [value]}]] + {:db (reduce #(put-or-dissoc %1 id (:key %2) (:value %2)) db value)})) (defn- append [acc k v] (let [o (get acc k)] @@ -190,13 +262,14 @@ (fn [_ [_ _ m]] {::arithmetic m})) -(defn button [{:keys [on-click disabled]} label] - [button/secondary-button (merge {:disabled? disabled} +(defn button [{:keys [on-click enabled disabled] :as m} label] + [button/secondary-button (merge {:disabled? (or (when (contains? m :enabled) (or (nil? enabled) (false? enabled))) disabled)} (when on-click {:on-press #(re-frame/dispatch (on-click {}))})) label]) -(defn input [{:keys [on-change placeholder]}] - [react/text-input (merge {:placeholder placeholder - :style {:width "100%"}} +(defn input [{:keys [keyboard-type style on-change placeholder]}] + [react/text-input (merge {:placeholder placeholder} + (when style {:style style}) + (when keyboard-type {:keyboard-type keyboard-type}) (when on-change {:on-change-text #(re-frame/dispatch (on-change {:value %}))}))]) @@ -256,8 +329,8 @@ 'text {:value text} 'touchable-opacity {:value touchable-opacity :properties {:on-press :event}} 'image {:value image :properties {:uri :string}} - 'input {:value input :properties {:on-change :event :placeholder :string}} - 'button {:value button :properties {:disabled :boolean :on-click :event}} + 'input {:value input :properties {:on-change :event :placeholder :string :keyboard-type :keyword}} + '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}} @@ -268,8 +341,14 @@ :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? :numeric}} 'wallet/tokens {:value :extensions.wallet/tokens :arguments {:filter :vector}}} - :events {'alert + :events {'identity + {:permissions [:read] + :value :extensions/identity-event + :arguments {:cb :event}} + 'alert {:permissions [:read] :value :alert :arguments {:value :string}} @@ -303,6 +382,16 @@ :arguments {:values #{:plus :minus :times :divide} :operation :keyword :on-result :event}} + 'schedule/start + {:permissions [:read] + :value :extensions/schedule-start + :arguments {:interval :number + :on-created :event + :on-result :event}} + 'schedule/cancel + {:permissions [:read] + :value :extensions/schedule-cancel + :arguments {:value :number}} 'json/parse {:permissions [:read] :value :extensions/json-parse @@ -316,11 +405,15 @@ 'store/put {:permissions [:read] :value :store/put - :arguments {:key :string :value :map}} + :arguments {:key :string :value :any}} + 'store/puts + {:permissions [:read] + :value :store/puts + :arguments {:value :vector}} 'store/append {:permissions [:read] :value :store/append - :arguments {:key :string :value :map}} + :arguments {:key :string :value :any}} 'store/clear {:permissions [:read] :value :store/clear @@ -346,6 +439,11 @@ :arguments {:hash :string :on-success :event :on-failure? :event}} + 'ethereum/transaction-receipt + {:permissions [:read] + :value :extensions/ethereum-transaction-receipt + :arguments {:value :string + :on-result :event}} 'ethereum/send-transaction {:permissions [:read] :value :extensions/ethereum-send-transaction diff --git a/src/status_im/extensions/ethereum.cljs b/src/status_im/extensions/ethereum.cljs index 5b851a0358..22932504da 100644 --- a/src/status_im/extensions/ethereum.cljs +++ b/src/status_im/extensions/ethereum.cljs @@ -99,6 +99,44 @@ (fn [{db :db} [_ _ {:keys [to] :as arguments}]] (wrap-with-resolution db arguments :to execute-ethcall))) +(defn- parse-log [{:keys [address transactionHash blockHash transactionIndex topics blockNumber logIndex removed data]}] + {: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 + (let [{:keys [status transactionHash transactionIndex blockHash blockNumber from to cumulativeGasUsed gasUsed contractAddress logs logsBloom]} m] + {:status (abi-spec/hex-to-number status) + :transaction-hash transactionHash + :transaction-index (abi-spec/hex-to-number transactionIndex) + :block-hash blockHash + :block-number (abi-spec/hex-to-number blockNumber) + :from from + :to to + :cumulative-gas-used (abi-spec/hex-to-number cumulativeGasUsed) + :gas-used (abi-spec/hex-to-number gasUsed) + :contract-address contractAddress + :logs (map parse-log logs) + :logs-bloom logsBloom}))) + +(handlers/register-handler-fx + :extensions/ethereum-transaction-receipt + (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] @@ -158,11 +196,11 @@ (handlers/register-handler-fx :extensions/ethereum-resolve-ens - (fn [{db :db} [_ _ {:keys [name on-result] :as arguments}]] + (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-result {:result %})))) - (re-frame/dispatch (on-result {:error (str "'" name "' is not a valid name")}))))) \ No newline at end of file + (re-frame/dispatch (on-result {:error (str "'" name "' is not a valid name")}))))) diff --git a/src/status_im/utils/ethereum/abi_spec.cljs b/src/status_im/utils/ethereum/abi_spec.cljs index d67bb966c1..ad5669572b 100644 --- a/src/status_im/utils/ethereum/abi_spec.cljs +++ b/src/status_im/utils/ethereum/abi_spec.cljs @@ -37,7 +37,8 @@ (.hexToUtf8 utils (str "0x" x))) (defn hex-to-number [x] - (.toNumber (dependencies/Web3.prototype.toBigNumber (str "0x" x) 16))) + (when x + (.toNumber (dependencies/Web3.prototype.toBigNumber (str "0x" x) 16)))) (defn sha3 [s] (.sha3 utils (str s))) diff --git a/src/status_im/utils/ethereum/tokens.cljs b/src/status_im/utils/ethereum/tokens.cljs index 115b6f80ac..c66620dae0 100644 --- a/src/status_im/utils/ethereum/tokens.cljs +++ b/src/status_im/utils/ethereum/tokens.cljs @@ -523,6 +523,9 @@ (defn nfts-for [all-tokens chain] (filter :nft? (tokens-for all-tokens chain))) +(defn token-for [chain all-tokens token] + (some #(when (= token (name (:symbol %))) %) (tokens-for all-tokens chain))) + (defn sorted-tokens-for [all-tokens chain] (->> (tokens-for all-tokens chain) (filter #(not (:hidden? %))) diff --git a/src/status_im/utils/money.cljs b/src/status_im/utils/money.cljs index feaba86486..875be659c9 100644 --- a/src/status_im/utils/money.cljs +++ b/src/status_im/utils/money.cljs @@ -94,11 +94,13 @@ (defn token->unit [n decimals] (when-let [bn (bignumber n)] - (.dividedBy bn (bignumber (from-decimal decimals))))) + (when-let [d (from-decimal decimals)] + (.dividedBy bn (bignumber d))))) (defn unit->token [n decimals] (when-let [bn (bignumber n)] - (.times bn (bignumber (from-decimal decimals))))) + (when-let [d (from-decimal decimals)] + (.times bn (bignumber d))))) ;;NOTE(goranjovic) - We have two basic representations of values that refer to cryptocurrency amounts: formatted and ;; internal. Formatted representation is the one we show on screens and include in reports, whereas internal diff --git a/test/cljs/status_im/test/utils/ethereum/abi_spec.cljs b/test/cljs/status_im/test/utils/ethereum/abi_spec.cljs index 65e46d90f2..35e9a0456e 100644 --- a/test/cljs/status_im/test/utils/ethereum/abi_spec.cljs +++ b/test/cljs/status_im/test/utils/ethereum/abi_spec.cljs @@ -2,6 +2,9 @@ (:require [cljs.test :refer-macros [deftest is testing]] [status-im.utils.ethereum.abi-spec :as abi-spec])) +(deftest enc + (is (= "000000000000000000000000eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" (abi-spec/enc {:type :address :value "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"})))) + (deftest test-encode (is (= (abi-spec/encode "baz(uint32,bool)" [69 true]) "0xcdcd77c000000000000000000000000000000000000000000000000000000000000000450000000000000000000000000000000000000000000000000000000000000001"))