Revert "[Fixes #7052 and #7037] Improved extensions ethereum support"

This reverts commit 6a8c9bf14f.

Signed-off-by: Igor Mandrigin <i@mandrigin.ru>
This commit is contained in:
Igor Mandrigin 2018-12-21 13:34:11 +01:00
parent 6a8c9bf14f
commit 794313dbee
No known key found for this signature in database
GPG Key ID: 4A0EDDE26E66BC8B
8 changed files with 302 additions and 341 deletions

View File

@ -11,7 +11,7 @@
com.taoensso/timbre {:mvn/version "4.10.0"} com.taoensso/timbre {:mvn/version "4.10.0"}
hickory {:mvn/version "0.7.1"} hickory {:mvn/version "0.7.1"}
com.cognitect/transit-cljs {:mvn/version "0.8.248"} 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"} mvxcvi/alphabase {:mvn/version "1.0.0"}
rasom/cljs-react-navigation {:mvn/version "0.1.4"}} rasom/cljs-react-navigation {:mvn/version "0.1.4"}}

View File

@ -11,7 +11,7 @@
[com.taoensso/timbre "4.10.0"] [com.taoensso/timbre "4.10.0"]
[hickory "0.7.1"] [hickory "0.7.1"]
[com.cognitect/transit-cljs "0.8.248"] [com.cognitect/transit-cljs "0.8.248"]
[status-im/pluto "iteration-4-6"] [status-im/pluto "iteration-4-5"]
[mvxcvi/alphabase "1.0.0"] [mvxcvi/alphabase "1.0.0"]
[rasom/cljs-react-navigation "0.1.4"]] [rasom/cljs-react-navigation "0.1.4"]]
:plugins [[lein-cljsbuild "1.1.7"] :plugins [[lein-cljsbuild "1.1.7"]

View File

@ -114,17 +114,17 @@
(def command-hook (def command-hook
"Hook for extensions" "Hook for extensions"
{:properties {:properties
{:description? :string {:description? :string
:scope #{:personal-chats :public-chats :group-chats} :scope #{:personal-chats :public-chats}
:short-preview? :view :short-preview :view
:preview? :view :preview :view
:on-send? :event :on-send? :event
:on-receive? :event :on-receive? :event
:on-send-sync? :event :on-send-sync? :event
:parameters? [{:id :keyword :parameters? [{:id :keyword
:type {:one-of #{:text :phone :password :number}} :type {:one-of #{:text :phone :password :number}}
:placeholder :string :placeholder :string
:suggestions? :view}]} :suggestions? :view}]}
:hook :hook
(reify hooks/Hook (reify hooks/Hook
(hook-in [_ id {extension-id :id} {:keys [description scope parameters preview short-preview (hook-in [_ id {extension-id :id} {:keys [description scope parameters preview short-preview
@ -138,8 +138,8 @@
(validate [_ _ _]) (validate [_ _ _])
(on-send [_ command-message _] (when on-send {:dispatch (on-send command-message)})) (on-send [_ command-message _] (when on-send {:dispatch (on-send command-message)}))
(on-receive [_ command-message _] (when on-receive {:dispatch (on-receive command-message)})) (on-receive [_ command-message _] (when on-receive {:dispatch (on-receive command-message)}))
(short-preview [_ props] (when short-preview (short-preview props))) (short-preview [_ props] (short-preview props))
(preview [_ props] (when preview (preview props))) (preview [_ props] (preview props))
protocol/Yielding protocol/Yielding
(yield-control [_ props _] {:dispatch (on-send-sync props)}) (yield-control [_ props _] {:dispatch (on-send-sync props)})
protocol/Extension protocol/Extension
@ -152,8 +152,8 @@
(validate [_ _ _]) (validate [_ _ _])
(on-send [_ command-message _] (when on-send {:dispatch (on-send command-message)})) (on-send [_ command-message _] (when on-send {:dispatch (on-send command-message)}))
(on-receive [_ command-message _] (when on-receive {:dispatch (on-receive command-message)})) (on-receive [_ command-message _] (when on-receive {:dispatch (on-receive command-message)}))
(short-preview [_ props] (when short-preview (short-preview props))) (short-preview [_ props] (short-preview props))
(preview [_ props] (when preview (preview props))) (preview [_ props] (preview props))
protocol/Extension protocol/Extension
(extension-id [_] extension-id)))] (extension-id [_] extension-id)))]
(load-commands cofx [new-command]))) (load-commands cofx [new-command])))

View File

@ -8,7 +8,6 @@
[status-im.chat.commands.impl.transactions :as transactions] [status-im.chat.commands.impl.transactions :as transactions]
[status-im.ui.components.button.view :as button] [status-im.ui.components.button.view :as button]
[status-im.ui.components.checkbox.view :as checkbox] [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.list.views :as list]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui.screens.wallet.settings.views :as settings] [status-im.ui.screens.wallet.settings.views :as settings]
@ -22,7 +21,8 @@
status-im.extensions.ethereum status-im.extensions.ethereum
[status-im.utils.ethereum.tokens :as tokens] [status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.ethereum.core :as ethereum] [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 (re-frame/reg-fx
::identity-event ::identity-event
@ -90,7 +90,7 @@
:<- [:balance] :<- [:balance]
(fn [[all-tokens network balance] [_ _ {token :token}]] (fn [[all-tokens network balance] [_ _ {token :token}]]
(let [{:keys [decimals]} (get-token-for network all-tokens 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 (money/token->unit value decimals)
:value-in-wei value}))) :value-in-wei value})))
@ -98,26 +98,20 @@
:extensions.wallet/token :extensions.wallet/token
:<- [:wallet/all-tokens] :<- [:wallet/all-tokens]
:<- [:network] :<- [: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)] (let [{:keys [decimals] :as m} (get-token-for network all-tokens token)]
(merge m (merge m
(when amount {:amount-in-wei (money/unit->token amount decimals)}) (when amount {:amount (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))
(re-frame/reg-sub (re-frame/reg-sub
:extensions.wallet/tokens :extensions.wallet/tokens
:<- [:wallet/all-tokens] :<- [:wallet/all-tokens]
:<- [:wallet/visible-tokens-symbols]
:<- [:network] :<- [:network]
(fn [[all-tokens visible-tokens-symbols network] [_ _ {filter-vector :filter visible :visible}]] (fn [[all-tokens network] [_ _ {filter-vector :filter}]]
(let [tokens (map normalize-token (filter #(and (not (:nft? %)) (if visible (contains? visible-tokens-symbols (:symbol %)) true)) (let [tokens (tokens/sorted-tokens-for all-tokens (ethereum/network->chain-keyword network))]
(tokens/sorted-tokens-for all-tokens (ethereum/network->chain-keyword network))))] (if (= :all (first filter-vector))
(if filter-vector tokens
(filter #((set filter-vector) (:symbol %)) tokens) (filter #((set filter-vector) (:symbol %)) tokens)))))
tokens))))
(re-frame/reg-sub (re-frame/reg-sub
:store/get :store/get
@ -366,57 +360,43 @@
(defn picker [{:keys [style on-change selected enabled data]}] (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}]) [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] (defn text [o & children]
(if (map? o) (if (map? o)
(into [react/text o] (map wrap-text-child children)) [react/text o children]
(into [react/text {} o] (map wrap-text-child children)))) (into [react/text {} o] children)))
(defn- wrap-view-child [child] (defn- wrap-view-child [child]
(if (vector? child) child [text {} 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] (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] (defn icon [o]
(apply abstract-view react/scroll-view o children)) [icons/icon (:key o) o])
(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])
(def capacities (def capacities
{:components {'view {:value view} {:components {'view {:value view}
'scroll-view {:value scroll-view} 'text {:value text}
'keyboard-avoiding-view {:value react/keyboard-avoiding-view} 'touchable-opacity {:value touchable-opacity :properties {:on-press :event}}
'text {:value text} 'icon {:value icon :properties {:key :keyword :color :any}}
'touchable-opacity {:value touchable-opacity :properties {:on-press :event}} 'image {:value image :properties {:uri :string :source :numeric}}
'icon {:value icon :properties {:key :keyword :color :any}} 'input {:value input :properties {:on-change :event :change-delay :number :placeholder :string :keyboard-type :keyword :placeholder-text-color :any}}
'image {:value image :properties {:uri :string :source :string}} 'button {:value button :properties {:enabled :boolean :disabled :boolean :on-click :event}}
'input {:value input :properties {:on-change :event :placeholder :string :keyboard-type :keyword :change-delay? :number :placeholder-text-color :any}} 'link {:value link :properties {:uri :string}}
'button {:value button :properties {:enabled :boolean :disabled :boolean :on-click :event}} 'list {:value list :properties {:data :vector :item-view :view :key? :keyword}}
'link {:value link :properties {:uri :string}} 'checkbox {:value checkbox :properties {:on-change :event :checked :boolean}}
'list {:value list :properties {:data :vector :item-view :view :key? :keyword}} 'activity-indicator {:value activity-indicator :properties {:animating :boolean :color :string :size :keyword :hides-when-stopped :boolean}}
'checkbox {:value checkbox :properties {:on-change :event :checked :boolean}} 'picker {:value picker :properties {:on-change :event :selected :string :enabled :boolean :data :vector}}
'activity-indicator {:value activity-indicator :properties {:animating :boolean :color :string :size :keyword :hides-when-stopped :boolean}} 'nft-token-viewer {:value transactions/nft-token :properties {:token :string}}
'picker {:value picker :properties {:on-change :event :selected :string :enabled :boolean :data :vector}} 'transaction-status {:value transactions/transaction-status :properties {:outgoing :string :tx-hash :string}}}
'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}} :queries {'identity {:value :extensions/identity :arguments {:value :map}}
'store/get {:value :store/get :arguments {:key :string}} 'store/get {:value :store/get :arguments {:key :string}}
'wallet/collectibles {:value :get-collectible-token :arguments {:token :string :symbol :string}} 'wallet/collectibles {:value :get-collectible-token :arguments {:token :string :symbol :string}}
'wallet/balance {:value :extensions.wallet/balance :arguments {:token :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/token {:value :extensions.wallet/token :arguments {:token :string :amount? :numeric}}
'wallet/tokens {:value :extensions.wallet/tokens :arguments {:filter? :vector :visible? :boolean}}} 'wallet/tokens {:value :extensions.wallet/tokens :arguments {:filter :vector}}}
:events {'identity :events {'identity
{:permissions [:read] {:permissions [:read]
:value :extensions/identity-event :value :extensions/identity-event
@ -457,7 +437,7 @@
{:permissions [:read] {:permissions [:read]
:value :extensions/arithmetic :value :extensions/arithmetic
:arguments {:values :vector :arguments {:values :vector
:operation {:one-of #{:plus :minus :times :divide}} :operation :keyword
:on-result :event}} :on-result :event}}
'schedule/start 'schedule/start
{:permissions [:read] {:permissions [:read]
@ -525,158 +505,140 @@
'ethereum/transaction-receipt 'ethereum/transaction-receipt
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-transaction-receipt :value :extensions/ethereum-transaction-receipt
:arguments {:value :string :arguments {:value :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum/sign 'ethereum/sign
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-sign :value :extensions/ethereum-sign
:arguments {:message? :string :arguments {:message? :string
:data? :string :data? :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum/send-transaction 'ethereum/send-transaction
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-send-transaction :value :extensions/ethereum-send-transaction
:arguments {:to :string :arguments {:to :string
:gas? :string :gas? :string
:gas-price? :string :gas-price? :string
:value? :string :value? :string
:method? :string :method? :string
:params? :vector :params? :vector
:nonce? :string :nonce? :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum/logs 'ethereum/logs
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-logs :value :extensions/ethereum-logs
:arguments {:from? :string :arguments {:fromBlock? :string
:to? :string :toBlock? :string
:address? :vector :address? :vector
:topics? :vector :topics? :vector
:block-hash? :string :blockhash? :string
:on-success :event :on-result :event}}
:on-failure? :event}} 'ethereum/resolve-ens
{:permissions [:read]
:value :extensions/ethereum-resolve-ens
:arguments {:name :string
:on-result :event}}
'ethereum/create-filter 'ethereum/create-filter
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-create-filter :value :extensions/ethereum-create-filter
:arguments {:type {:one-of #{:filter :block :pending-transaction}} :arguments {:filter-type :string
:from? :string :from? :string
:to? :string :to? :string
:address? :vector :address? :vector
:topics? :vector :topics? :vector
:block-hash? :string :block-hash? :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum/logs-changes 'ethereum/logs-changes
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-logs-changes :value :extensions/ethereum-logs-changes
:arguments {:id :string}} :arguments {:id :string}}
'ethereum/cancel-filter 'ethereum/cancel-filter
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-cancel-filter :value :extensions/ethereum-cancel-filter
:arguments {:id :string}} :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 'ethereum.erc20/total-supply
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc20-total-supply :value :extensions/ethereum-erc20-total-supply
:arguments {:contract :string :arguments {:contract :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc20/balance-of 'ethereum.erc20/balance-of
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc20-balance-of :value :extensions/ethereum-erc20-balance-of
:arguments {:contract :string :arguments {:contract :string
:token-owner :string :token-owner :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc20/transfer 'ethereum.erc20/transfer
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc20-transfer :value :extensions/ethereum-erc20-transfer
:arguments {:contract :string :arguments {:contract :string
:to :string :to :string
:value :number :value :number
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc20/transfer-from 'ethereum.erc20/transfer-from
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc20-transfer-from :value :extensions/ethereum-erc20-transfer-from
:arguments {:contract :string :arguments {:contract :string
:from :string :from :string
:to :string :to :string
:value :number :value :number
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc20/approve 'ethereum.erc20/approve
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc20-approve :value :extensions/ethereum-erc20-approve
:arguments {:contract :string :arguments {:contract :string
:spender :string :spender :string
:value :number :value :number
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc20/allowance 'ethereum.erc20/allowance
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc20-allowance :value :extensions/ethereum-erc20-allowance
:arguments {:contract :string :arguments {:contract :string
:token-owner :string :token-owner :string
:spender :string :spender :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc721/owner-of 'ethereum.erc721/owner-of
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc721-owner-of :value :extensions/ethereum-erc721-owner-of
:arguments {:contract :string :arguments {:contract :string
:token-id :string :token-id :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc721/is-approved-for-all 'ethereum.erc721/is-approved-for-all
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc721-is-approved-for-all :value :extensions/ethereum-erc721-is-approved-for-all
:arguments {:contract :string :arguments {:contract :string
:owner :string :owner :string
:operator :string :operator :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc721/get-approved 'ethereum.erc721/get-approved
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc721-get-approved :value :extensions/ethereum-erc721-get-approved
:arguments {:contract :string :arguments {:contract :string
:token-id :string :token-id :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc721/set-approval-for-all 'ethereum.erc721/set-approval-for-all
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc721-set-approval-for-all :value :extensions/ethereum-erc721-set-approval-for-all
:arguments {:contract :string :arguments {:contract :string
:operator :string :operator :string
:approved :boolean :approved :boolean
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum.erc721/safe-transfer-from 'ethereum.erc721/safe-transfer-from
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-erc721-safe-transfer-from :value :extensions/ethereum-erc721-safe-transfer-from
:arguments {:contract :string :arguments {:contract :string
:from :string :from :string
:to :string :to :string
:token-id :string :token-id :string
:data? :string :data? :string
:on-success :event :on-result :event}}
:on-failure? :event}}
'ethereum/call 'ethereum/call
{:permissions [:read] {:permissions [:read]
:value :extensions/ethereum-call :value :extensions/ethereum-call
:arguments {:to :string :arguments {:to :string
:method :string :method :string
:params? :vector :params? :vector
:outputs? :vector :outputs? :vector
:on-success :event :on-result :event}}}
:on-failure? :event}}}
:hooks {:chat.command commands/command-hook :hooks {:chat.command commands/command-hook
:wallet.settings settings/hook}}) :wallet.settings settings/hook}})

View File

@ -17,20 +17,23 @@
[status-im.native-module.core :as status])) [status-im.native-module.core :as status]))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/wallet-ui-on-success :extensions/wallet-ui-on-result
(fn [cofx [_ on-success _ result _]] (fn [cofx [_ on-result id result method]]
(fx/merge cofx (fx/merge cofx
{:dispatch (on-success {:value result})} (when on-result
(navigation/navigate-back)))) {: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 (handlers/register-handler-fx
:extensions/wallet-ui-on-failure :extensions/wallet-ui-on-error
(fn [_ [_ on-failure message]] (fn [{db :db} [_ on-result message]]
(when on-failure {:dispatch (on-failure {:value message})}))) (when on-result {:dispatch (on-result {:error message :result nil})})))
(defn- wrap-with-resolution [db arguments address-keyword f] (defn- wrap-with-resolution [db arguments address-keyword f]
"function responsible to resolve ens taken from argument "funtction responsible to resolve ens taken from argument
and call the specified function with resolved address" and call the specified function with resolved address"
(let [address (get arguments address-keyword) (let [address (get arguments address-keyword)
first-address (if (vector? address) ;; currently we only support one ens for address first-address (if (vector? address) ;; currently we only support one ens for address
(first address) (first address)
@ -43,7 +46,8 @@
(ens/get-addr web3 registry first-address #(f db (assoc arguments address-keyword %)))) (ens/get-addr web3 registry first-address #(f db (assoc arguments address-keyword %))))
(f db arguments)))) (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 (let [{:keys [to value data gas gasPrice nonce]} params
contact (get contacts (hex/normalize-hex to))] contact (get contacts (hex/normalize-hex to))]
(cond-> {:id "extension-id" (cond-> {:id "extension-id"
@ -62,15 +66,15 @@
:gas-price (when gasPrice :gas-price (when gasPrice
(money/bignumber gasPrice)) (money/bignumber gasPrice))
:data data :data data
:on-result [:extensions/wallet-ui-on-success on-success] :on-result [:extensions/wallet-ui-on-result on-result]
:on-error [:extensions/wallet-ui-on-failure on-failure]} :on-error [:extensions/wallet-ui-on-error on-result]}
nonce nonce
(assoc :nonce 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]) (let [tx-object (assoc (select-keys arguments [:to :gas :gas-price :value :nonce])
:data (when (and method params) (abi-spec/encode method params))) :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))) (models.wallet/open-modal-wallet-for-transaction db transaction tx-object)))
(handlers/register-handler-fx (handlers/register-handler-fx
@ -78,173 +82,143 @@
(fn [{db :db} [_ _ arguments]] (fn [{db :db} [_ _ arguments]]
(wrap-with-resolution db arguments :to execute-send-transaction))) (wrap-with-resolution db arguments :to execute-send-transaction)))
(defn- rpc-args [method params] (defn- execute-ethcall [_ {:keys [to method params outputs on-result]}]
{: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]}]
(let [tx-object {:to to :data (when method (abi-spec/encode method params))}] (let [tx-object {:to to :data (when method (abi-spec/encode method params))}]
{:browser/call-rpc [(rpc-args "eth_call" [tx-object "latest"]) {:browser/call-rpc [{"jsonrpc" "2.0"
#(rpc-dispatch %1 %2 (fn [o] (parse-call-result o outputs)) on-success on-failure)]})) "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 (handlers/register-handler-fx
:extensions/ethereum-call :extensions/ethereum-call
(fn [{db :db} [_ _ arguments]] (fn [{db :db} [_ _ {:keys [to] :as arguments}]]
(wrap-with-resolution db arguments :to execute-ethcall))) (wrap-with-resolution db arguments :to execute-ethcall)))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc20-total-supply :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 (let [json-rpc-args {:to contract
:method "totalSupply()" :method "totalSupply()"
:outputs ["uint256"] :outputs ["uint256"]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (wrap-with-resolution db json-rpc-args :to execute-ethcall))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc20-balance-of :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 (let [json-rpc-args {:to contract
:method "balanceOf(address)" :method "balanceOf(address)"
:params [token-owner] :params [token-owner]
:outputs ["uint256"] :outputs ["uint256"]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (wrap-with-resolution db json-rpc-args :to execute-ethcall))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc20-allowance :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 (let [json-rpc-args {:to contract
:method "allowance(address,address)" :method "allowance(address,address)"
:params [token-owner spender] :params [token-owner spender]
:outputs ["uint256"] :outputs ["uint256"]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (wrap-with-resolution db json-rpc-args :to execute-ethcall))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc20-transfer :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 (let [json-rpc-args {:to contract
:method "transfer(address,uint256)" :method "transfer(address,uint256)"
:params [to value] :params [to value]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (wrap-with-resolution db json-rpc-args :to execute-send-transaction))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc20-transfer-from :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 (let [json-rpc-args {:to contract
:method "transferFrom(address,address,uint256)" :method "transferFrom(address,address,uint256)"
:params [from to value] :params [from to value]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (wrap-with-resolution db json-rpc-args :to execute-send-transaction))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc20-approve :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 (let [json-rpc-args {:to contract
:method "approve(address,uint256)" :method "approve(address,uint256)"
:params [spender value] :params [spender value]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (wrap-with-resolution db json-rpc-args :to execute-send-transaction))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc721-owner-of :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 (let [json-rpc-args {:to contract
:method "ownerOf(uint256)" :method "ownerOf(uint256)"
:params [token-id] :params [token-id]
:outputs ["address"] :outputs ["address"]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (wrap-with-resolution db json-rpc-args :to execute-ethcall))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc721-is-approved-for-all :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 (let [json-rpc-args {:to contract
:method "isApprovedForAll(address,address)" :method "isApprovedForAll(address,address)"
:params [owner operator] :params [owner operator]
:outputs ["bool"] :outputs ["bool"]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (wrap-with-resolution db json-rpc-args :to execute-ethcall))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc721-get-approved :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 (let [json-rpc-args {:to contract
:method "getApproved(uint256)" :method "getApproved(uint256)"
:params [token-id] :params [token-id]
:outputs ["address"] :outputs ["address"]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-ethcall)))) (wrap-with-resolution db json-rpc-args :to execute-ethcall))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc721-set-approval-for-all :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 (let [json-rpc-args {:to contract
:method "setApprovalForAll(address,bool)" :method "setApprovalForAll(address,bool)"
:params [to approved] :params [to approved]
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (wrap-with-resolution db json-rpc-args :to execute-send-transaction))))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-erc721-safe-transfer-from :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 (let [json-rpc-args {:to contract
:method (if data :method (if data
"safeTransferFrom(address,address,uint256,bytes)" "safeTransferFrom(address,address,uint256,bytes)"
"safeTransferFrom(address,address,uint256)") "safeTransferFrom(address,address,uint256)")
:params (if data :params (if data
[from to token-id data] [from to token-id data]
[from to token-id]) [from to token-id])
:on-success on-success :on-result on-result}]
:on-failure on-failure}]
(wrap-with-resolution db json-rpc-args :to execute-send-transaction)))) (wrap-with-resolution db json-rpc-args :to execute-send-transaction))))
(defn- parse-log [{:keys [address transactionHash blockHash transactionIndex topics blockNumber logIndex removed data]}] (defn- parse-log [{:keys [address transactionHash blockHash transactionIndex topics blockNumber logIndex removed data]}]
(merge {:data data {:address address
:topics topics :transaction-hash transactionHash
:address address :blockHash blockHash
:removed removed} :transaction-index (abi-spec/hex-to-number transactionIndex)
;; TODO parse data and topics, filter useless solidity first topic, aggregate as events ? :topics topics ;; TODO parse topics
(when logIndex {:log-index (abi-spec/hex-to-number logIndex)}) :block-number (abi-spec/hex-to-number blockNumber)
(when transactionIndex {:transaction-index (abi-spec/hex-to-number transactionIndex)}) :log-index (abi-spec/hex-to-number logIndex)
(when transactionHash {:transaction-hash transactionHash}) :removed removed
(when blockHash {:block-hash blockHash}) :data data})
(when blockNumber {:block-number (abi-spec/hex-to-number blockNumber)})))
(defn- parse-receipt [m] (defn- parse-receipt [m]
(when m (when m
@ -264,8 +238,16 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-transaction-receipt :extensions/ethereum-transaction-receipt
(fn [_ [_ _ {:keys [value] :as m}]] (fn [_ [_ _ {:keys [value on-result]}]]
(rpc-call constants/web3-transaction-receipt [value] parse-receipt m))) (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] (defn- event-topic-enc [event params]
(let [eventid (str event "(" (string/join "," params) ")")] (let [eventid (str event "(" (string/join "," params) ")")]
@ -282,16 +264,17 @@
:else :bytes)) :else :bytes))
(defn- values-topic-enc [type values] (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 (cond
(or (nil? t) (string? t)) t ;; nil topic ;; immediate topic (extension encode topic by its own) ;; vector of immediate topics (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 (map? t) ;; simplified topic interface, we need to encode
(let [{:keys [event params type values]} t] (let [{:keys [event params type values]} t]
(cond (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 (some? type) (values-topic-enc type values) ;; indexed values topic
:else nil)) ;; error :else nil)) ;; error
:else nil)) :else nil))
@ -302,13 +285,19 @@
(re-matches #"^[0-9]+$" block) (str "0x" (abi-spec/number-to-hex block)) (re-matches #"^[0-9]+$" block) (str "0x" (abi-spec/number-to-hex block))
:else block)) :else block))
(defn- execute-get-logs [_ {:keys [from to address topics block-hash] :as m}] (defn- execute-get-logs [_ {:keys [fromBlock toBlock address topics blockhash on-result]}]
(let [params [{:from (ensure-hex-bn from) (let [parsed-topics (mapv parse-topic topics)
:to (ensure-hex-bn to) args {:jsonrpc "2.0"
:address address :method constants/web3-get-logs
:topics (generate-topic topics) :params [{:fromBlock (ensure-hex-bn fromBlock)
:blockhash block-hash}]] :toBlock (ensure-hex-bn toBlock)
(rpc-call constants/web3-get-logs params #(map parse-log %) m))) :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 (handlers/register-handler-fx
:extensions/ethereum-logs :extensions/ethereum-logs
@ -317,59 +306,71 @@
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-resolve-ens :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) (if (ens/is-valid-eth-name? name)
(let [{:keys [web3 network]} db (let [{:keys [web3 network]} db
network-info (get-in db [:account/account :networks network]) network-info (get-in db [:account/account :networks network])
chain (ethereum/network->chain-keyword network-info) chain (ethereum/network->chain-keyword network-info)
registry (get ens/ens-registries chain)] registry (get ens/ens-registries chain)]
(ens/get-addr web3 registry name #(re-frame/dispatch (on-success {:value %})))) (ens/get-addr web3 registry name #(re-frame/dispatch (on-result {:result %}))))
(when on-failure (re-frame/dispatch (on-result {:error (str "'" name "' is not a valid name")})))))
(re-frame/dispatch (on-failure {:value (str "'" name "' is not a valid name")}))))))
;; EXTENSION SIGN -> SIGN MESSAGE ;; EXTENSION SIGN -> SIGN MESSAGE
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-sign :extensions/ethereum-sign
(fn [{db :db :as cofx} [_ _ {:keys [message data id on-success on-failure]}]] (fn [{db :db :as cofx} [_ _ {:keys [message data id on-result]}]]
(if (and message data) (if (= (nil? message) (nil? data))
(when on-failure {:dispatch (on-result {:error "only one of :message and :data can be used"})}
{:dispatch (on-failure {:error "only one of :message and :data can be used"})})
(fx/merge cofx (fx/merge cofx
{:db (assoc-in db [:wallet :send-transaction] {:db (assoc-in db [:wallet :send-transaction]
{:id id {: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))) :data (or data (str "0x" (abi-spec/from-utf8 message)))
:on-result [:extensions/wallet-ui-on-success on-success] :on-result [:extensions/wallet-ui-on-result on-result]
:on-error [:extensions/wallet-ui-on-failure on-failure] :on-error [:extensions/wallet-ui-on-error on-result]
:method constants/web3-personal-sign})} :method constants/web3-personal-sign})}
(navigation/navigate-to-cofx :wallet-sign-message-modal nil))))) (navigation/navigate-to-cofx :wallet-sign-message-modal nil)))))
;; poll logs implementation
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-logs-changes :extensions/ethereum-logs-changes
(fn [_ [_ _ {:keys [id] :as m}]] (fn [_ [_ _ {:keys [filterId on-result]}]]
(rpc-call constants/web3-get-filter-changes [(abi-spec/number-to-hex id)] #(map parse-log %) m))) (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 (handlers/register-handler-fx
:extensions/ethereum-cancel-filter :extensions/ethereum-cancel-filter
(fn [_ [_ _ {:keys [id] :as m}]] (fn [_ [_ _ {:keys [filterId on-result]}]]
(rpc-call constants/web3-uninstall-filter [(abi-spec/number-to-hex id)] #(abi-spec/hex-to-value % "bool") m))) (let [args {:jsonrpc "2.0"
:method constants/web3-uninstall-filter
(defn create-filter-method [type] :params [filterId]}
(case type payload (types/clj->json args)]
:filter constants/web3-new-filter (status/call-private-rpc payload #(let [{:keys [error result]} (types/json->clj %1)
:block constants/web3-new-block-filter response (merge {:result result} (when error {:error error}))]
:pending-transaction constants/web3-new-pending-transaction-filter)) (re-frame/dispatch (on-result response)))))))
(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}]))
(handlers/register-handler-fx (handlers/register-handler-fx
:extensions/ethereum-create-filter :extensions/ethereum-create-filter
(fn [_ [_ _ {:keys [type] :as m}]] (fn [_ [_ _ {:keys [filter-type from to address topics block-hash on-result]}]]
(rpc-call (create-filter-method type) (create-filter-arguments type m) abi-spec/hex-to-number m))) (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)))))))

View File

@ -15,9 +15,8 @@
hooks (get-in account [:extensions extension-id :hooks])] hooks (get-in account [:extensions extension-id :hooks])]
(apply fx/merge cofx (apply fx/merge cofx
(mapcat (fn [[_ extension-hooks]] (mapcat (fn [[_ extension-hooks]]
(map (fn [[hook-id {parsed :parsed {hook :hook} :hook-ref}]] (map (fn [[hook-id {:keys [hook-ref parsed]}]]
(when hook (partial hook-fn (:hook hook-ref) hook-id {:id extension-id} parsed))
(partial hook-fn hook hook-id {:id extension-id} parsed)))
extension-hooks)) extension-hooks))
hooks)))) hooks))))

View File

@ -18,12 +18,13 @@
"Hook for extensions" "Hook for extensions"
{:properties {:properties
{:label :string {:label :string
:view :view} :view :view
:on-click? :event}
:hook :hook
(reify hooks/Hook (reify hooks/Hook
(hook-in [_ id _ {:keys [label view _]} {:keys [db]}] (hook-in [_ id env {:keys [label view on-click]} {:keys [db]}]
{:db (assoc-in db [:wallet :settings id] {:label label :view view})}) {:db (assoc-in db [:wallet :settings id] {:label label :view view :on-click on-click})})
(unhook [_ id _ _ {:keys [db]}] (unhook [_ id env _ {:keys [db]}]
{:db (update-in db [:wallet :settings] dissoc id)}))}) {:db (update-in db [:wallet :settings] dissoc id)}))})
(defn- render-token [{:keys [symbol name icon]} visible-tokens] (defn- render-token [{:keys [symbol name icon]} visible-tokens]
@ -56,8 +57,7 @@
:render-fn #(render-token % visible-tokens)}]]])) :render-fn #(render-token % visible-tokens)}]]]))
(defview settings-hook [] (defview settings-hook []
(letsubs [{:keys [label view]} [:get-screen-params :wallet-settings-hook] (letsubs [{:keys [label view]} [:get-screen-params :wallet-settings-hook]]
{address :address} [:account/account]]
[react/view {:style {:flex 1 :background-color colors/white}} [react/view {:style {:flex 1 :background-color colors/white}}
[status-bar/status-bar {:type :modal-wallet}] [status-bar/status-bar {:type :modal-wallet}]
[toolbar/toolbar {:style wallet.styles/toolbar} [toolbar/toolbar {:style wallet.styles/toolbar}
@ -65,11 +65,14 @@
(re-frame/dispatch [:navigate-back])))] (re-frame/dispatch [:navigate-back])))]
[toolbar/content-title {:color colors/white} [toolbar/content-title {:color colors/white}
label]] 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 {: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 [] (defview toolbar-view []
(letsubs [settings [:wallet/settings]] (letsubs [settings [:wallet/settings]]

View File

@ -22,20 +22,16 @@
(.leftPad utils x (+ len to-pad))))) (.leftPad utils x (+ len to-pad)))))
(defn to-two-complement [x] (defn to-two-complement [x]
(when x (subs (.toTwosComplement utils x) 2))
(subs (.toTwosComplement utils x) 2)))
(defn from-utf8 [x] (defn from-utf8 [x]
(when x (subs (.fromUtf8 utils x) 2))
(subs (.fromUtf8 utils x) 2)))
(defn bytes-to-hex [x] (defn bytes-to-hex [x]
(when x (subs (.bytesToHex utils x) 2))
(subs (.bytesToHex utils x) 2)))
(defn number-to-hex [x] (defn number-to-hex [x]
(when x (subs (.numberToHex utils x) 2))
(subs (.numberToHex utils x) 2)))
(defn hex-to-utf8 [x] (defn hex-to-utf8 [x]
(.hexToUtf8 utils (str "0x" x))) (.hexToUtf8 utils (str "0x" x)))