[refactoring] remove web3, clean up wallet effects
- introduce json-rpc namespace, which provides `call` and `eth-call`, a generic way of calling a json-rpc method taking care of conversions and error handling - remove web3 usage from wallet - clean up effects, reducing the amount of computations when login in
This commit is contained in:
parent
89680f4861
commit
fc4c772c0b
|
@ -85,9 +85,10 @@
|
|||
(fx/defn initialize-wallet [cofx]
|
||||
(fx/merge cofx
|
||||
(wallet/initialize-tokens)
|
||||
(wallet/update-balances)
|
||||
(wallet/update-prices)
|
||||
(transactions/initialize)
|
||||
(ethereum.subscriptions/initialize)
|
||||
(wallet/update-wallet)))
|
||||
(ethereum.subscriptions/initialize)))
|
||||
|
||||
(fx/defn user-login [{:keys [db] :as cofx} create-database?]
|
||||
(let [{:keys [address password]} (accounts.db/credentials cofx)]
|
||||
|
|
|
@ -333,8 +333,8 @@
|
|||
;; TODO(janherich) - refactor wallet send events, updating gas price
|
||||
;; is generic thing which shouldn't be defined in wallet.send, then
|
||||
;; we can include the utility helper without running into circ-dep problem
|
||||
:update-gas-price {:web3 (:web3 db)
|
||||
:success-event :wallet/update-gas-price-success
|
||||
:wallet/update-gas-price
|
||||
{:success-event :wallet/update-gas-price-success
|
||||
:edit? false}}
|
||||
(navigation/navigate-to-cofx next-view-id {}))))
|
||||
protocol/EnhancedParameters
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
(ns status-im.ethereum.contracts
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.utils.ethereum.abi-spec :as abi-spec]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.wallet.core :as wallet]))
|
||||
|
||||
(def contracts
|
||||
{:status/tribute-to-talk
|
||||
{:address
|
||||
{:mainnet nil
|
||||
:testnet "0x3da3fc53e24707f36c5b4433b442e896c4955f0e"
|
||||
:rinkeby nil}
|
||||
:methods
|
||||
{:get-manifest
|
||||
{:signature "getManifest(address)"
|
||||
:outputs ["bytes"]}
|
||||
:set-manifest
|
||||
{:signature "setManifest(bytes)"
|
||||
:write? true}}}})
|
||||
|
||||
(re-frame/reg-fx
|
||||
::call
|
||||
(fn [{:keys [address data callback]}]
|
||||
(ethereum/call {:to address
|
||||
:data data}
|
||||
callback)))
|
||||
|
||||
(defn get-contract-address
|
||||
[db contract]
|
||||
(let [chain-keyword (-> (get-in db [:account/account :networks (:network db)])
|
||||
ethereum/network->chain-keyword)]
|
||||
(get-in contracts [contract :address chain-keyword])))
|
||||
|
||||
(fx/defn call
|
||||
[{:keys [db] :as cofx}
|
||||
{:keys [contract contract-address method params
|
||||
callback on-result on-error details]}]
|
||||
(when-let [contract-address (or contract-address
|
||||
(get-contract-address db contract))]
|
||||
(let [{:keys [signature outputs write?]}
|
||||
(get-in contracts [contract :methods method])
|
||||
data (abi-spec/encode signature params)]
|
||||
(if write?
|
||||
(wallet/open-sign-transaction-flow
|
||||
cofx
|
||||
(merge {:to contract-address
|
||||
:data data
|
||||
:id "approve"
|
||||
:symbol :ETH
|
||||
:method "eth_sendTransaction"
|
||||
:amount (money/bignumber 0)
|
||||
:on-result on-result
|
||||
:on-error on-error}
|
||||
details))
|
||||
{::call {:address contract-address
|
||||
:data data
|
||||
:callback #(callback (if (empty? outputs)
|
||||
%
|
||||
(abi-spec/decode % outputs)))}}))))
|
|
@ -0,0 +1,74 @@
|
|||
(ns status-im.ethereum.json-rpc
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ethereum.decode :as decode]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.utils.ethereum.abi-spec :as abi-spec]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.utils.types :as types]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(def json-rpc-api
|
||||
{"eth_call" {}
|
||||
"eth_getBalance"
|
||||
{:on-result money/bignumber}
|
||||
"eth_estimateGas"
|
||||
{:on-result money/bignumber}
|
||||
"eth_gasPrice"
|
||||
{:on-result money/bignumber}
|
||||
"eth_getBlockByHash"
|
||||
{:on-result #(-> (update % :number decode/uint)
|
||||
(update :timestamp decode/uint))}
|
||||
"eth_getTransactionByHash" {}
|
||||
"eth_getTransactionReceipt" {}
|
||||
"eth_newBlockFilter" {:subscription? true}
|
||||
"eth_newFilter" {:subscription? true}})
|
||||
|
||||
(defn call
|
||||
[{:keys [method params on-success on-error]}]
|
||||
(when-let [method-options (json-rpc-api method)]
|
||||
(let [{:keys [id on-result subscription?]
|
||||
:or {on-result identity
|
||||
id 1
|
||||
params []}} method-options
|
||||
on-error (or on-error
|
||||
#(log/warn :json-rpc/error method :params params :error %))]
|
||||
(if (nil? method)
|
||||
(log/error :json-rpc/method-not-found method)
|
||||
(status/call-private-rpc
|
||||
(types/clj->json {:jsonrpc "2.0"
|
||||
:id id
|
||||
:method (if subscription?
|
||||
"eth_subscribeSignal"
|
||||
method)
|
||||
:params (if subscription?
|
||||
[method params]
|
||||
params)})
|
||||
(fn [response]
|
||||
(if (string/blank? response)
|
||||
(on-error {:message "Blank response"})
|
||||
(let [{:keys [error result] :as response2} (types/json->clj response)]
|
||||
(if error
|
||||
(on-error error)
|
||||
(if subscription?
|
||||
(re-frame/dispatch
|
||||
[:ethereum.callback/subscription-success
|
||||
result on-success])
|
||||
(on-success (on-result result))))))))))))
|
||||
|
||||
(defn eth-call
|
||||
[{:keys [contract method params outputs on-success on-error block]
|
||||
:or {block "latest"
|
||||
params []}}]
|
||||
(call {:method "eth_call"
|
||||
:params [{:to contract
|
||||
:data (abi-spec/encode method params)}
|
||||
(if (int? block)
|
||||
(abi-spec/number-to-hex block)
|
||||
block)]
|
||||
:on-success
|
||||
(if outputs
|
||||
#(on-success (abi-spec/decode % outputs))
|
||||
on-success)
|
||||
:on-error
|
||||
on-error}))
|
|
@ -8,7 +8,8 @@
|
|||
[status-im.utils.ethereum.tokens :as tokens]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.types :as types]
|
||||
[taoensso.timbre :as log]))
|
||||
[taoensso.timbre :as log]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]))
|
||||
|
||||
(fx/defn handle-signal
|
||||
[cofx {:keys [subscription_id data] :as event}]
|
||||
|
@ -56,33 +57,14 @@
|
|||
;; from etherscan
|
||||
(transactions/initialize))))))
|
||||
|
||||
(defn subscribe-signal
|
||||
[filter params callback]
|
||||
(status/call-private-rpc
|
||||
(types/clj->json {:jsonrpc "2.0"
|
||||
:id 1
|
||||
:method "eth_subscribeSignal"
|
||||
:params [filter params]})
|
||||
(fn [response]
|
||||
(if (string/blank? response)
|
||||
(log/error ::subscription-unknown-error :filter filter :params params)
|
||||
(let [{:keys [error result]}
|
||||
(-> (.parse js/JSON response)
|
||||
(js->clj :keywordize-keys true))]
|
||||
(if error
|
||||
(log/error ::subscription-error error :filter filter :params params)
|
||||
(re-frame/dispatch [:ethereum.callback/subscription-success
|
||||
result
|
||||
callback])))))))
|
||||
|
||||
(defn new-token-transaction-filter
|
||||
[{:keys [chain-tokens from to] :as args}]
|
||||
(subscribe-signal
|
||||
"eth_newFilter"
|
||||
[{:fromBlock "latest"
|
||||
(json-rpc/call
|
||||
{:method "eth_newFilter"
|
||||
:params [{:fromBlock "latest"
|
||||
:toBlock "latest"
|
||||
:topics [constants/event-transfer-hash from to]}]
|
||||
(transactions/inbound-token-transfer-handler chain-tokens)))
|
||||
:on-success (transactions/inbound-token-transfer-handler chain-tokens)}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:ethereum.subscriptions/token-transactions
|
||||
|
@ -95,13 +77,16 @@
|
|||
|
||||
(defn new-block-filter
|
||||
[]
|
||||
(subscribe-signal
|
||||
"eth_newBlockFilter" []
|
||||
(json-rpc/call
|
||||
{:method "eth_newBlockFilter"
|
||||
:on-success
|
||||
(fn [[block-hash]]
|
||||
(transactions/get-block-by-hash
|
||||
block-hash
|
||||
(json-rpc/call
|
||||
{:method "eth_getBlockByHash"
|
||||
:params [block-hash true]
|
||||
:on-success
|
||||
(fn [block]
|
||||
(re-frame/dispatch [:ethereum.signal/new-block block]))))))
|
||||
(re-frame/dispatch [:ethereum.signal/new-block block]))}))}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:ethereum.subscriptions/new-block
|
||||
|
|
|
@ -1,68 +1,22 @@
|
|||
(ns status-im.ethereum.transactions.core
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ethereum.decode :as decode]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.ethereum.transactions.etherscan :as transactions.etherscan]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.types :as types]
|
||||
[status-im.wallet.core :as wallet]
|
||||
[taoensso.timbre :as log]))
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.wallet.core :as wallet]))
|
||||
|
||||
(def confirmations-count-threshold 12)
|
||||
|
||||
(defn get-block-by-hash
|
||||
[block-hash callback]
|
||||
(status/call-private-rpc
|
||||
(types/clj->json {:jsonrpc "2.0"
|
||||
:id 1
|
||||
:method "eth_getBlockByHash"
|
||||
:params [block-hash true]})
|
||||
(fn [response]
|
||||
(if (string/blank? response)
|
||||
(log/warn :web3-response-error)
|
||||
(callback (-> (.parse js/JSON response)
|
||||
(js->clj :keywordize-keys true)
|
||||
:result
|
||||
(update :number decode/uint)
|
||||
(update :timestamp decode/uint)))))))
|
||||
|
||||
(defn get-transaction-by-hash
|
||||
[transaction-hash callback]
|
||||
(status/call-private-rpc
|
||||
(types/clj->json {:jsonrpc "2.0"
|
||||
:id 1
|
||||
:method "eth_getTransactionByHash"
|
||||
:params [transaction-hash]})
|
||||
(fn [response]
|
||||
(if (string/blank? response)
|
||||
(log/warn :web3-response-error)
|
||||
(callback (-> (.parse js/JSON response)
|
||||
(js->clj :keywordize-keys true)
|
||||
:result))))))
|
||||
|
||||
(defn get-transaction-receipt [transaction-hash callback]
|
||||
(status/call-private-rpc
|
||||
(types/clj->json {:jsonrpc "2.0"
|
||||
:id 1
|
||||
:method "eth_getTransactionReceipt"
|
||||
:params [transaction-hash]})
|
||||
(fn [response]
|
||||
(if (string/blank? response)
|
||||
(log/warn :web3-response-error)
|
||||
(callback (-> (.parse js/JSON response)
|
||||
(js->clj :keywordize-keys true)
|
||||
:result))))))
|
||||
|
||||
(defn add-padding [address]
|
||||
{:pre [(string? address)]}
|
||||
(str "0x000000000000000000000000" (subs address 2)))
|
||||
|
||||
(defn- remove-padding [topic]
|
||||
{:pre [(string? topic)]}
|
||||
(str "0x" (subs topic 26)))
|
||||
(defn- remove-padding [address]
|
||||
{:pre [(string? address)]}
|
||||
(str "0x" (subs address 26)))
|
||||
|
||||
(def default-erc20-token
|
||||
{:symbol :ERC20
|
||||
|
@ -80,7 +34,7 @@
|
|||
:symbol symbol
|
||||
:from (remove-padding from)
|
||||
:to (remove-padding to)
|
||||
:value (ethereum/hex->bignumber data)
|
||||
:value (money/bignumber data)
|
||||
:type direction
|
||||
:token token
|
||||
:error? false
|
||||
|
@ -96,8 +50,10 @@
|
|||
[chain-tokens
|
||||
{:keys [number timestamp]}
|
||||
{:keys [transfer direction hash gasPrice value gas from input nonce to] :as transaction}]
|
||||
(get-transaction-receipt
|
||||
hash
|
||||
(json-rpc/call
|
||||
{:method "eth_getTransactionReceipt"
|
||||
:params [hash]
|
||||
:on-success
|
||||
(fn [{:keys [gasUsed logs] :as receipt}]
|
||||
(let [[event _ _] (:topics (first logs))
|
||||
transfer (= constants/event-transfer-hash event)]
|
||||
|
@ -120,7 +76,7 @@
|
|||
:from from
|
||||
:to to
|
||||
:type direction
|
||||
:value (str (decode/uint value))}))])))))
|
||||
:value (str (decode/uint value))}))])))}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:ethereum.transactions/enrich-transactions-from-new-blocks
|
||||
|
@ -153,16 +109,22 @@
|
|||
;; This function takes the map of supported tokens as params and returns a
|
||||
;; handler for token transfer events
|
||||
(doseq [[block-hash block-transfers] transfers-by-block]
|
||||
(get-block-by-hash
|
||||
block-hash
|
||||
(json-rpc/call
|
||||
{:method "eth_getBlockByHash"
|
||||
:params [block-hash false]
|
||||
:on-success
|
||||
(fn [{:keys [timestamp number]}]
|
||||
(let [timestamp (str (* timestamp 1000))]
|
||||
(doseq [{:keys [hash] :as transfer} block-transfers]
|
||||
(get-transaction-by-hash
|
||||
hash
|
||||
(json-rpc/call
|
||||
{:method "eth_getTransactionByHash"
|
||||
:params [hash]
|
||||
:on-success
|
||||
(fn [{:keys [gasPrice gas input nonce]}]
|
||||
(get-transaction-receipt
|
||||
hash
|
||||
(json-rpc/call
|
||||
{:method "eth_getTransactionReceipt"
|
||||
:params [hash]
|
||||
:on-success
|
||||
(fn [{:keys [gasUsed]}]
|
||||
(re-frame/dispatch
|
||||
[:ethereum.transactions/new
|
||||
|
@ -174,7 +136,7 @@
|
|||
:gas-price (str (decode/uint gasPrice))
|
||||
:gas-limit (str (decode/uint gas))
|
||||
:data input
|
||||
:nonce (str (decode/uint nonce))))])))))))))))))
|
||||
:nonce (str (decode/uint nonce))))]))}))}))))})))))
|
||||
|
||||
;; -----------------------------------------------
|
||||
;; transactions api
|
||||
|
@ -184,7 +146,7 @@
|
|||
[{:keys [db] :as cofx} {:keys [hash] :as transaction}]
|
||||
(fx/merge cofx
|
||||
{:db (assoc-in db [:wallet :transactions hash] transaction)}
|
||||
wallet/update-wallet))
|
||||
wallet/update-balances))
|
||||
|
||||
(fx/defn handle-history
|
||||
[{:keys [db] :as cofx} transactions]
|
||||
|
@ -192,7 +154,7 @@
|
|||
{:db (update-in db
|
||||
[:wallet :transactions]
|
||||
#(merge transactions %))}
|
||||
wallet/update-wallet))
|
||||
wallet/update-balances))
|
||||
|
||||
(fx/defn handle-token-history
|
||||
[{:keys [db]} transactions]
|
||||
|
|
|
@ -979,11 +979,6 @@
|
|||
(fn [cofx [_ error sync]]
|
||||
(web3/update-syncing-progress cofx error sync)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:web3.callback/get-block-number
|
||||
(fn [cofx [_ error block-number]]
|
||||
(node/update-block-number cofx error block-number)))
|
||||
|
||||
;; notifications module
|
||||
|
||||
(handlers/register-handler-fx
|
||||
|
@ -2152,7 +2147,7 @@
|
|||
(handlers/register-handler-fx
|
||||
:wallet.ui/pull-to-refresh
|
||||
(fn [cofx _]
|
||||
(wallet/update-wallet cofx)))
|
||||
(wallet/update-prices cofx)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:wallet.transactions/add-filter
|
||||
|
@ -2180,11 +2175,6 @@
|
|||
(fn [cofx [_ symbol balance]]
|
||||
(wallet/configure-token-balance-and-visibility cofx symbol balance)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:TODO.remove/update-wallet
|
||||
(fn [cofx _]
|
||||
(wallet/update-wallet cofx)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:wallet.settings.ui/navigate-back-pressed
|
||||
(fn [cofx [_ on-close]]
|
||||
|
@ -2192,7 +2182,7 @@
|
|||
(when on-close
|
||||
{:dispatch on-close})
|
||||
(navigation/navigate-back)
|
||||
(wallet/update-wallet))))
|
||||
(wallet/update-balances))))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:wallet.callback/update-balance-success
|
||||
|
@ -2217,7 +2207,7 @@
|
|||
(handlers/register-handler-fx
|
||||
:wallet.callback/update-prices-success
|
||||
(fn [cofx [_ prices]]
|
||||
(wallet/update-prices cofx prices)))
|
||||
(wallet/on-update-prices-success cofx prices)))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
:wallet.callback/update-prices-fail
|
||||
|
@ -2242,8 +2232,8 @@
|
|||
(handlers/register-handler-fx
|
||||
:TODO.remove/update-estimated-gas
|
||||
(fn [{:keys [db]} [_ obj]]
|
||||
{:update-estimated-gas {:web3 (:web3 db)
|
||||
:obj obj
|
||||
{:wallet/update-estimated-gas
|
||||
{:obj obj
|
||||
:success-event :wallet/update-estimated-gas-success}}))
|
||||
|
||||
(handlers/register-handler-fx
|
||||
|
|
|
@ -157,11 +157,6 @@
|
|||
{:error error}
|
||||
(when sync-state (js->clj sync-state :keywordize-keys true))))})
|
||||
|
||||
(fx/defn update-block-number
|
||||
[{:keys [db]} error block-number]
|
||||
(when-not error
|
||||
{:db (assoc db :node/latest-block-number block-number)}))
|
||||
|
||||
(fx/defn start
|
||||
[{:keys [db]} address]
|
||||
(let [network (if address
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
[{{:keys [web3] :as db} :db :as cofx}]
|
||||
(if (:account/account db)
|
||||
{:web3/get-syncing web3
|
||||
:web3/get-block-number web3
|
||||
:utils/dispatch-later [{:ms 10000
|
||||
:dispatch [:protocol/state-sync-timed-out]}]}
|
||||
(semaphores/free cofx :check-sync-state?)))
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
[status-im.ui.screens.navigation :as navigation]
|
||||
[status-im.utils.ethereum.abi-spec :as abi-spec]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.ethereum.erc20 :as erc20]
|
||||
[status-im.utils.ethereum.stickers :as ethereum.stickers]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.money :as money]
|
||||
|
@ -74,13 +73,18 @@
|
|||
(when on-result {:on-result on-result})
|
||||
tx))
|
||||
|
||||
(def snt-contracts
|
||||
{:mainnet "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
|
||||
:testnet "0xc55cf4b03948d7ebc8b9e8bad92643703811d162"
|
||||
:rinkeby nil})
|
||||
|
||||
(fx/defn approve-pack [{db :db} pack-id price]
|
||||
(let [network (get-in db [:account/account :networks (:network db)])
|
||||
address (ethereum/normalized-address (get-in db [:account/account :address]))
|
||||
chain (ethereum/network->chain-keyword network)
|
||||
stickers-contract (get ethereum.stickers/contracts chain)
|
||||
data (abi-spec/encode "buyToken(uint256,address)" [pack-id address])
|
||||
tx-object {:to (get erc20/snt-contracts chain)
|
||||
tx-object {:to (get snt-contracts chain)
|
||||
:data (abi-spec/encode "approveAndCall(address,uint256,bytes)" [stickers-contract price data])}]
|
||||
(wallet/open-modal-wallet-for-transaction
|
||||
db
|
||||
|
|
|
@ -1273,15 +1273,17 @@
|
|||
send-transaction
|
||||
edit)))
|
||||
|
||||
(defn check-sufficient-funds [transaction balance symbol amount]
|
||||
(defn check-sufficient-funds
|
||||
[transaction balance symbol amount]
|
||||
(assoc transaction :sufficient-funds?
|
||||
(or (nil? amount)
|
||||
(money/sufficient-funds? amount (get balance symbol)))))
|
||||
|
||||
(defn check-sufficient-gas [transaction balance symbol amount]
|
||||
(defn check-sufficient-gas
|
||||
[transaction balance symbol amount]
|
||||
(assoc transaction :sufficient-gas?
|
||||
(or (nil? amount)
|
||||
(let [available-ether (get balance :ETH (money/bignumber 0))
|
||||
(let [available-ether (money/bignumber (get balance :ETH 0))
|
||||
available-for-gas (if (= :ETH symbol)
|
||||
(.minus available-ether (money/bignumber amount))
|
||||
available-ether)]
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
[re-frame.core :as re-frame]
|
||||
[status-im.accounts.update.core :as accounts.update]
|
||||
[status-im.contact.db :as contact.db]
|
||||
[status-im.ethereum.contracts :as contracts]
|
||||
[status-im.ipfs.core :as ipfs]
|
||||
[status-im.tribute-to-talk.db :as tribute-to-talk.db]
|
||||
[status-im.ui.screens.navigation :as navigation]
|
||||
[status-im.utils.contenthash :as contenthash]
|
||||
[status-im.utils.ethereum.contracts :as contracts]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.fx :as fx]
|
||||
[taoensso.timbre :as log]))
|
||||
|
|
|
@ -9,4 +9,4 @@
|
|||
new-settings (assoc-in settings [:wallet :currency] currency)]
|
||||
(fx/merge cofx
|
||||
(accounts.update/update-settings new-settings {})
|
||||
(wallet/update-wallet))))
|
||||
(wallet/update-prices))))
|
||||
|
|
|
@ -101,7 +101,6 @@
|
|||
|
||||
;; contents of eth_syncing or `nil` if the node isn't syncing now
|
||||
(spec/def :node/chain-sync-state (spec/nilable map?))
|
||||
(spec/def :node/latest-block-number (spec/nilable number?))
|
||||
|
||||
;;;;NAVIGATION
|
||||
|
||||
|
@ -234,7 +233,6 @@
|
|||
:node/restart?
|
||||
:node/address
|
||||
:node/chain-sync-state
|
||||
:node/latest-block-number
|
||||
:universal-links/url
|
||||
:push-notifications/stored
|
||||
:browser/browsers
|
||||
|
|
|
@ -51,8 +51,8 @@
|
|||
;; NOTE(janherich) - whenever changing assets, we want to clear the previusly set amount/amount-text
|
||||
(defn changed-asset [{:keys [db] :as fx} old-symbol new-symbol]
|
||||
(-> fx
|
||||
(merge {:update-gas-price {:web3 (:web3 db)
|
||||
:success-event :wallet/update-gas-price-success
|
||||
(merge {:wallet/update-gas-price
|
||||
{:success-event :wallet/update-gas-price-success
|
||||
:edit? false}})
|
||||
(assoc-in [:db :wallet :send-transaction :amount] nil)
|
||||
(assoc-in [:db :wallet :send-transaction :amount-text] nil)
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
[status-im.transport.utils :as transport.utils]
|
||||
[status-im.ui.screens.navigation :as navigation]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.ethereum.erc20 :as erc20]
|
||||
[status-im.utils.ethereum.tokens :as tokens]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.handlers :as handlers]
|
||||
|
@ -25,9 +24,17 @@
|
|||
(security/safe-unmask-data masked-password)
|
||||
on-completed))
|
||||
|
||||
(defn- send-tokens [all-tokens symbol chain {:keys [from to value gas gasPrice]} on-completed masked-password]
|
||||
(defn- send-tokens
|
||||
[all-tokens symbol chain
|
||||
{:keys [from to value gas gasPrice]} on-completed masked-password]
|
||||
(let [contract (:address (tokens/symbol->token all-tokens (keyword chain) symbol))]
|
||||
(erc20/transfer contract from to value gas gasPrice masked-password on-completed)))
|
||||
(status/send-transaction (types/clj->json
|
||||
(merge (ethereum/call-params contract "transfer(address,uint256)" to value)
|
||||
{:from from
|
||||
:gas gas
|
||||
:gasPrice gasPrice}))
|
||||
(security/safe-unmask-data masked-password)
|
||||
on-completed)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
::send-transaction
|
||||
|
@ -263,8 +270,8 @@
|
|||
|
||||
(defn update-gas-price
|
||||
([db edit? success-event]
|
||||
{:update-gas-price {:web3 (:web3 db)
|
||||
:success-event (or success-event :wallet/update-gas-price-success)
|
||||
{:wallet/update-gas-price
|
||||
{:success-event (or success-event :wallet/update-gas-price-success)
|
||||
:edit? edit?}})
|
||||
([db edit?] (update-gas-price db edit? :wallet/update-gas-price-success))
|
||||
([db] (update-gas-price db false :wallet/update-gas-price-success)))
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
(ns status-im.utils.ethereum.contracts
|
||||
(:require [re-frame.core :as re-frame]
|
||||
[status-im.utils.ethereum.abi-spec :as abi-spec]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.wallet.core :as wallet]))
|
||||
|
||||
(def contracts
|
||||
{:status/snt
|
||||
{:address
|
||||
{:mainnet "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
|
||||
:testnet "0xc55cf4b03948d7ebc8b9e8bad92643703811d162"
|
||||
:rinkeby nil}
|
||||
:methods
|
||||
{:erc20/transfer
|
||||
{:signature "transfer(address,uint256)"
|
||||
:write? true}}}
|
||||
:status/tribute-to-talk
|
||||
{:address
|
||||
{:mainnet nil
|
||||
:testnet "0x3da3fc53e24707f36c5b4433b442e896c4955f0e"
|
||||
:rinkeby nil}
|
||||
:methods
|
||||
{:get-manifest
|
||||
{:signature "getManifest(address)"
|
||||
:return-params ["bytes"]}
|
||||
:set-manifest
|
||||
{:signature "setManifest(bytes)"
|
||||
:write? true}}}
|
||||
:status/sticker-market
|
||||
{:address
|
||||
{:mainnet nil
|
||||
:testnet "0x39d16CdB56b5a6a89e1A397A13Fe48034694316E"
|
||||
:rinkeby nil}
|
||||
:methods
|
||||
{:pack-count
|
||||
{:signature "packCount()"
|
||||
:return-params ["uint256"]}
|
||||
:pack-data
|
||||
{:signature "getPackData(uint256)"
|
||||
:return-params ["bytes4[]" "address" "bool" "uint256" "uint256" "bytes"]}}}})
|
||||
|
||||
(re-frame/reg-fx
|
||||
::call
|
||||
(fn [{:keys [address data callback]}]
|
||||
(ethereum/call {:to address
|
||||
:data data}
|
||||
callback)))
|
||||
|
||||
(fx/defn call
|
||||
[{:keys [db] :as cofx}
|
||||
{:keys [contract method params callback on-result on-error details]}]
|
||||
(let [chain-keyword (-> (get-in db [:account/account :networks (:network db)])
|
||||
ethereum/network->chain-keyword)
|
||||
contract-address (get-in contracts [contract :address chain-keyword])]
|
||||
(when contract-address
|
||||
(let [{:keys [signature return-params write?]}
|
||||
(get-in contracts [contract :methods method])
|
||||
data (abi-spec/encode signature params)]
|
||||
(if write?
|
||||
(wallet/open-sign-transaction-flow
|
||||
cofx
|
||||
(merge {:to contract-address
|
||||
:data data
|
||||
:id "approve"
|
||||
:symbol :ETH
|
||||
:method "eth_sendTransaction"
|
||||
:amount (money/bignumber 0)
|
||||
:on-result on-result
|
||||
:on-error on-error}
|
||||
details))
|
||||
{::call {:address contract-address
|
||||
:data data
|
||||
:callback #(callback (if (empty? return-params)
|
||||
%
|
||||
(abi-spec/decode % return-params)))}})))))
|
|
@ -1,12 +1,9 @@
|
|||
(ns status-im.utils.ethereum.core
|
||||
(:require [clojure.string :as string]
|
||||
[status-im.ethereum.decode :as decode]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.js-dependencies :as dependencies]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.utils.ethereum.tokens :as tokens]
|
||||
[status-im.utils.ethereum.abi-spec :as abi-spec]
|
||||
[status-im.utils.money :as money]
|
||||
[taoensso.timbre :as log]))
|
||||
[status-im.utils.money :as money]))
|
||||
|
||||
;; IDs standardized in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md#list-of-chain-ids
|
||||
|
||||
|
@ -65,6 +62,12 @@
|
|||
network->chain-keyword
|
||||
name))
|
||||
|
||||
(defn chain-keyword
|
||||
[db]
|
||||
(let [network-id (get-in db [:account/account :network])
|
||||
network (get-in db [:account/account :networks network-id])]
|
||||
(network->chain-keyword network)))
|
||||
|
||||
(defn sha3
|
||||
([s]
|
||||
(.sha3 dependencies/Web3.prototype (str s)))
|
||||
|
@ -127,77 +130,23 @@
|
|||
(apply str (take 10 (sha3 signature))))
|
||||
|
||||
(defn call [params callback]
|
||||
(status/call-private-rpc
|
||||
(.stringify js/JSON (clj->js {:jsonrpc "2.0"
|
||||
:id 1
|
||||
:method "eth_call"
|
||||
:params [params "latest"]}))
|
||||
(fn [response]
|
||||
(if (string/blank? response)
|
||||
(log/warn :web3-response-error)
|
||||
(callback (get (js->clj (.parse js/JSON response)) "result"))))))
|
||||
(json-rpc/call
|
||||
{:method "eth_call"
|
||||
:params [params "latest"]
|
||||
:on-success callback}))
|
||||
|
||||
(defn call-params [contract method-sig & params]
|
||||
(let [data (apply format-call-params (sig->method-id method-sig) params)]
|
||||
{:to contract :data data}))
|
||||
|
||||
(defn send-transaction [web3 params cb]
|
||||
(.sendTransaction (.-eth web3) (clj->js params) cb))
|
||||
|
||||
(def default-transaction-gas (money/bignumber 21000))
|
||||
|
||||
(defn gas-price [web3 cb]
|
||||
(.getGasPrice (.-eth web3) cb))
|
||||
|
||||
(defn estimate-gas-web3 [web3 obj cb]
|
||||
(try
|
||||
(.estimateGas (.-eth web3) obj cb)
|
||||
(catch :default _)))
|
||||
|
||||
(defn estimate-gas [symbol]
|
||||
(if (tokens/ethereum? symbol)
|
||||
default-transaction-gas
|
||||
;; TODO(jeluard) Rely on estimateGas call
|
||||
(.times default-transaction-gas 5)))
|
||||
|
||||
(defn handle-error [error]
|
||||
(log/info (.stringify js/JSON error)))
|
||||
|
||||
(defn get-block-number [web3 cb]
|
||||
(.getBlockNumber (.-eth web3)
|
||||
(fn [error result]
|
||||
(if-not error
|
||||
(cb result)
|
||||
(handle-error error)))))
|
||||
|
||||
(defn get-block-info [web3 number cb]
|
||||
(.getBlock (.-eth web3) number (fn [error result]
|
||||
(if-not error
|
||||
(cb (js->clj result :keywordize-keys true))
|
||||
(handle-error error)))))
|
||||
|
||||
(defn get-transaction [transaction-hash callback]
|
||||
(status/call-private-rpc
|
||||
(.stringify js/JSON (clj->js {:jsonrpc "2.0"
|
||||
:id 1
|
||||
:method "eth_getTransactionByHash"
|
||||
:params [transaction-hash]}))
|
||||
(fn [response]
|
||||
(if (string/blank? response)
|
||||
(log/warn :web3-response-error)
|
||||
(callback (-> (.parse js/JSON response)
|
||||
(js->clj :keywordize-keys true)
|
||||
:result
|
||||
(update :gasPrice decode/uint)
|
||||
(update :value decode/uint)
|
||||
(update :gas decode/uint)))))))
|
||||
|
||||
(defn get-transaction-receipt [web3 number cb]
|
||||
(.getTransactionReceipt (.-eth web3) number (fn [error result]
|
||||
(if-not error
|
||||
(cb (js->clj result :keywordize-keys true))
|
||||
(handle-error error)))))
|
||||
|
||||
(defn address= [address1 address2]
|
||||
(and address1 address2
|
||||
(= (normalized-address address1)
|
||||
|
|
|
@ -1,174 +0,0 @@
|
|||
(ns status-im.utils.ethereum.erc20
|
||||
"
|
||||
Helper functions to interact with [ERC20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) smart contract
|
||||
|
||||
Example
|
||||
|
||||
Contract: https://ropsten.etherscan.io/address/0x29b5f6efad2ad701952dfde9f29c960b5d6199c5#readContract
|
||||
Owner: https://ropsten.etherscan.io/token/0x29b5f6efad2ad701952dfde9f29c960b5d6199c5?a=0xa7cfd581060ec66414790691681732db249502bd
|
||||
|
||||
With a running node on Ropsten:
|
||||
(let [web3 (:web3 @re-frame.db/app-db)
|
||||
contract \"0x29b5f6efad2ad701952dfde9f29c960b5d6199c5\"
|
||||
address \"0xa7cfd581060ec66414790691681732db249502bd\"]
|
||||
(erc20/balance-of web3 contract address println))
|
||||
|
||||
=> 29166666
|
||||
"
|
||||
(:require [clojure.string :as string]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.ethereum.abi-spec :as abi-spec]
|
||||
[status-im.native-module.core :as status]
|
||||
[status-im.utils.security :as security]
|
||||
[status-im.js-dependencies :as dependencies]
|
||||
[status-im.utils.types :as types])
|
||||
(:refer-clojure :exclude [name symbol]))
|
||||
|
||||
(def utils dependencies/web3-utils)
|
||||
|
||||
(def snt-contracts
|
||||
{:mainnet "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
|
||||
:testnet "0xc55cf4b03948d7ebc8b9e8bad92643703811d162"
|
||||
:rinkeby nil})
|
||||
|
||||
(def abi
|
||||
(clj->js
|
||||
[{:constant true
|
||||
:inputs []
|
||||
:name "name"
|
||||
:outputs [{:name ""
|
||||
:type "string"}]
|
||||
:payable false
|
||||
:stateMutability "view"
|
||||
:type "function"}
|
||||
{:constant true
|
||||
:inputs []
|
||||
:name "symbol"
|
||||
:outputs [{:name ""
|
||||
:type "string"}]
|
||||
:payable false
|
||||
:stateMutability "view"
|
||||
:type "function"}
|
||||
{:constant true
|
||||
:inputs []
|
||||
:name "decimals"
|
||||
:outputs [{:name ""
|
||||
:type "uint8"}]
|
||||
:payable false
|
||||
:stateMutability "view"
|
||||
:type "function"}
|
||||
{:constant true
|
||||
:inputs [{:name "_who"
|
||||
:type "address"}]
|
||||
:name "balanceOf"
|
||||
:outputs [{:name ""
|
||||
:type "uint256"}]
|
||||
:payable false
|
||||
:stateMutability "view"
|
||||
:type "function"}
|
||||
{:constant true
|
||||
:inputs []
|
||||
:name "totalSupply"
|
||||
:outputs [{:name ""
|
||||
:type "uint256"}],
|
||||
:payable false
|
||||
:stateMutability "view"
|
||||
:type "function"}
|
||||
{:constant false
|
||||
:inputs [{:name "_to"
|
||||
:type "address"}
|
||||
{:name "_value"
|
||||
:type "uint256"}]
|
||||
:name "transfer"
|
||||
:outputs [{:name ""
|
||||
:type "bool"}],
|
||||
:payable false
|
||||
:stateMutability "nonpayable"
|
||||
:type "function"}
|
||||
{:anonymous false
|
||||
:inputs [{:indexed true
|
||||
:name "from"
|
||||
:type "address"},
|
||||
{:indexed true
|
||||
:name "to"
|
||||
:type "address"},
|
||||
{:indexed false
|
||||
:name "value"
|
||||
:type "uint256"}]
|
||||
:name "Transfer"
|
||||
:type "event"}]))
|
||||
|
||||
(defn get-instance* [web3 contract]
|
||||
(.at (.contract (.-eth web3) abi) contract))
|
||||
|
||||
(def get-instance
|
||||
(memoize get-instance*))
|
||||
|
||||
(defn name [web3 contract cb]
|
||||
(.name (get-instance web3 contract) cb))
|
||||
|
||||
(defn symbol [web3 contract cb]
|
||||
(.symbol (get-instance web3 contract) cb))
|
||||
|
||||
(defn decimals [web3 contract cb]
|
||||
(.decimals (get-instance web3 contract) cb))
|
||||
|
||||
(defn total-supply [web3 contract cb]
|
||||
(.totalSupply (get-instance web3 contract) cb))
|
||||
|
||||
(defn balance-of [web3 contract address cb]
|
||||
(.balanceOf (get-instance web3 contract) address cb))
|
||||
|
||||
(defn transfer [contract from to value gas gas-price masked-password on-completed]
|
||||
(status/send-transaction (types/clj->json
|
||||
(merge (ethereum/call-params contract "transfer(address,uint256)" to value)
|
||||
{:from from
|
||||
:gas gas
|
||||
:gasPrice gas-price}))
|
||||
(security/safe-unmask-data masked-password)
|
||||
on-completed))
|
||||
|
||||
(defn transfer-from [web3 contract from-address to-address value cb]
|
||||
(ethereum/call (ethereum/call-params contract
|
||||
"transferFrom(address,address,uint256)"
|
||||
(ethereum/normalized-address from-address)
|
||||
(ethereum/normalized-address to-address)
|
||||
(ethereum/int->hex value))
|
||||
#(cb (ethereum/hex->boolean %))))
|
||||
|
||||
(defn approve [web3 contract address value cb]
|
||||
(ethereum/call (ethereum/call-params contract
|
||||
"approve(address,uint256)"
|
||||
(ethereum/normalized-address address)
|
||||
(ethereum/int->hex value))
|
||||
#(cb (ethereum/hex->boolean %))))
|
||||
|
||||
(defn allowance [web3 contract owner-address spender-address cb]
|
||||
(ethereum/call (ethereum/call-params contract
|
||||
"allowance(address,address)"
|
||||
(ethereum/normalized-address owner-address)
|
||||
(ethereum/normalized-address spender-address))
|
||||
#(cb (ethereum/hex->bignumber %))))
|
||||
|
||||
(defn is-transfer?
|
||||
[input-data]
|
||||
(string/starts-with? input-data "0xa9059cbb"))
|
||||
|
||||
(defn is-snt-contract?
|
||||
[contract]
|
||||
((into #{} (vals snt-contracts)) contract))
|
||||
|
||||
(defn get-transaction
|
||||
"only supports SNT for now"
|
||||
[transaction-hash callback]
|
||||
(ethereum/get-transaction
|
||||
transaction-hash
|
||||
(fn [{:keys [to input] :as result}]
|
||||
(when (and result
|
||||
(is-snt-contract? to)
|
||||
(is-transfer? input))
|
||||
(let [[recipient snt-value]
|
||||
(abi-spec/decode (subs input 10) ["address" "uint"])]
|
||||
(callback (assoc result
|
||||
:recipient recipient
|
||||
:snt-value snt-value)))))))
|
|
@ -3,13 +3,13 @@
|
|||
[re-frame.core :as re-frame]
|
||||
[status-im.accounts.update.core :as accounts.update]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.screens.navigation :as navigation]
|
||||
[status-im.ui.screens.wallet.utils :as wallet.utils]
|
||||
[status-im.utils.config :as config]
|
||||
[status-im.utils.core :as utils.core]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.ethereum.erc20 :as erc20]
|
||||
[status-im.utils.ethereum.tokens :as tokens]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.hex :as utils.hex]
|
||||
|
@ -18,30 +18,6 @@
|
|||
[status-im.utils.utils :as utils.utils]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(defn get-balance [{:keys [web3 account-id on-success on-error]}]
|
||||
(if (and web3 account-id)
|
||||
(.getBalance
|
||||
(.-eth web3)
|
||||
account-id
|
||||
(fn [err resp]
|
||||
(if-not err
|
||||
(on-success resp)
|
||||
(on-error err))))
|
||||
(on-error "web3 or account-id not available")))
|
||||
|
||||
(defn get-token-balance
|
||||
[{:keys [web3 contract account-id on-success on-error]}]
|
||||
(if (and web3 contract account-id)
|
||||
(erc20/balance-of
|
||||
web3
|
||||
contract
|
||||
(ethereum/normalized-address account-id)
|
||||
(fn [err resp]
|
||||
(if-not err
|
||||
(on-success resp)
|
||||
(on-error err))))
|
||||
(on-error "web3, contract or account-id not available")))
|
||||
|
||||
(defn assoc-error-message [db error-type err]
|
||||
(assoc-in db [:wallet :errors error-type] (or err :unknown-error)))
|
||||
|
||||
|
@ -83,27 +59,18 @@
|
|||
;; FX
|
||||
|
||||
(re-frame/reg-fx
|
||||
:get-balance
|
||||
(fn [{:keys [web3 account-id success-event error-event]}]
|
||||
(get-balance {:web3 web3
|
||||
:account-id account-id
|
||||
:on-success #(re-frame/dispatch [success-event %])
|
||||
:on-error #(re-frame/dispatch [error-event %])})))
|
||||
:wallet/get-balance
|
||||
(fn [{:keys [account-id on-success on-error]}]
|
||||
(json-rpc/call
|
||||
{:method "eth_getBalance"
|
||||
:params [account-id "latest"]
|
||||
:on-success on-success
|
||||
:on-error on-error})))
|
||||
|
||||
;; TODO(oskarth): At some point we want to get list of relevant
|
||||
;; assets to get prices for
|
||||
(re-frame/reg-fx
|
||||
:get-tokens-balance
|
||||
(fn [{:keys [web3 symbols all-tokens chain account-id success-event error-event]}]
|
||||
(doseq [symbol symbols]
|
||||
(let [contract (:address (tokens/symbol->token all-tokens chain symbol))]
|
||||
(get-token-balance {:web3 web3
|
||||
:contract contract
|
||||
:account-id account-id
|
||||
:on-success #(re-frame/dispatch [success-event symbol %])
|
||||
:on-error #(re-frame/dispatch [error-event symbol %])})))))
|
||||
|
||||
;; TODO(oskarth): At some point we want to get list of relevant assets to get prices for
|
||||
(re-frame/reg-fx
|
||||
:get-prices
|
||||
:wallet/get-prices
|
||||
(fn [{:keys [from to mainnet? success-event error-event chaos-mode?]}]
|
||||
(prices/get-prices from
|
||||
to
|
||||
|
@ -113,91 +80,99 @@
|
|||
chaos-mode?)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:update-gas-price
|
||||
(fn [{:keys [web3 success-event edit?]}]
|
||||
(ethereum/gas-price web3 #(re-frame/dispatch [success-event %2 edit?]))))
|
||||
:wallet/update-gas-price
|
||||
(fn [{:keys [success-event edit?]}]
|
||||
(json-rpc/call
|
||||
{:method "eth_gasPrice"
|
||||
:on-success
|
||||
#(re-frame/dispatch [success-event % edit?])})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:update-estimated-gas
|
||||
(fn [{:keys [web3 obj success-event]}]
|
||||
(ethereum/estimate-gas-web3 web3 (clj->js obj) #(re-frame/dispatch [success-event %2]))))
|
||||
:wallet/update-estimated-gas
|
||||
(fn [{:keys [obj success-event]}]
|
||||
(json-rpc/call
|
||||
{:method "eth_estimateGas"
|
||||
:params [obj]
|
||||
:on-success
|
||||
#(re-frame/dispatch [success-event %])})))
|
||||
|
||||
(defn- validate-token-name! [web3 {:keys [address symbol name]}]
|
||||
(erc20/name web3 address #(when (and (seq %2) ;;NOTE(goranjovic): skipping check if field not set in contract
|
||||
(not= name %2))
|
||||
(defn- validate-token-name!
|
||||
[{:keys [address symbol name]}]
|
||||
(json-rpc/eth-call
|
||||
{:contract address
|
||||
:method "name()"
|
||||
:outputs ["string"]
|
||||
:on-success
|
||||
(fn [[contract-name]]
|
||||
(when (and (not (empty? contract-name))
|
||||
(not= name contract-name))
|
||||
(let [message (i18n/label :t/token-auto-validate-name-error
|
||||
{:symbol symbol
|
||||
:expected name
|
||||
:actual %2
|
||||
:actual contract-name
|
||||
:address address})]
|
||||
(log/warn message)
|
||||
(utils.utils/show-popup (i18n/label :t/warning) message)))))
|
||||
(utils.utils/show-popup (i18n/label :t/warning) message))))}))
|
||||
|
||||
(defn- validate-token-symbol! [web3 {:keys [address symbol]}]
|
||||
(erc20/symbol web3 address #(when (and (seq %2) ;;NOTE(goranjovic): skipping check if field not set in contract
|
||||
(not= (clojure.core/name symbol) %2))
|
||||
(defn- validate-token-symbol!
|
||||
[{:keys [address symbol]}]
|
||||
(json-rpc/eth-call
|
||||
{:contract address
|
||||
:method "symbol()"
|
||||
:outputs ["string"]
|
||||
:on-success
|
||||
(fn [[contract-symbol]]
|
||||
;;NOTE(goranjovic): skipping check if field not set in contract
|
||||
(when (and (not (empty? contract-symbol))
|
||||
(not= (clojure.core/name symbol) contract-symbol))
|
||||
(let [message (i18n/label :t/token-auto-validate-symbol-error
|
||||
{:symbol symbol
|
||||
:expected (clojure.core/name symbol)
|
||||
:actual %2
|
||||
:actual contract-symbol
|
||||
:address address})]
|
||||
(log/warn message)
|
||||
(utils.utils/show-popup (i18n/label :t/warning) message)))))
|
||||
(utils.utils/show-popup (i18n/label :t/warning) message))))}))
|
||||
|
||||
(defn- validate-token-decimals! [web3 {:keys [address symbol decimals nft? skip-decimals-check?]}]
|
||||
;;NOTE(goranjovic): only skipping check if skip-decimals-check? flag is present because we can't differentiate
|
||||
;;between unset decimals and 0 decimals.
|
||||
(when-not skip-decimals-check?
|
||||
(erc20/decimals web3 address #(when (and %2
|
||||
(not nft?)
|
||||
(not= decimals (int %2)))
|
||||
(defn- validate-token-decimals!
|
||||
[{:keys [address symbol decimals nft?]}]
|
||||
(when-not nft?
|
||||
(json-rpc/eth-call
|
||||
{:contract address
|
||||
:method "decimals()"
|
||||
:outputs ["uint256"]
|
||||
:on-success
|
||||
(fn [[contract-decimals]]
|
||||
(when (and (not (nil? contract-decimals))
|
||||
(not= decimals contract-decimals))
|
||||
(let [message (i18n/label :t/token-auto-validate-decimals-error
|
||||
{:symbol symbol
|
||||
:expected decimals
|
||||
:actual %2
|
||||
:actual contract-decimals
|
||||
:address address})]
|
||||
(log/warn message)
|
||||
(utils.utils/show-popup (i18n/label :t/warning) message))))))
|
||||
(utils.utils/show-popup (i18n/label :t/warning) message))))})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wallet/validate-tokens
|
||||
(fn [{:keys [web3 tokens]}]
|
||||
(fn [tokens]
|
||||
(doseq [token tokens]
|
||||
(validate-token-decimals! web3 token)
|
||||
(validate-token-symbol! web3 token)
|
||||
(validate-token-name! web3 token))))
|
||||
(validate-token-decimals! token)
|
||||
(validate-token-symbol! token)
|
||||
(validate-token-name! token))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wallet/check-all-known-tokens-balance
|
||||
(fn [{:keys [web3 contracts account]}]
|
||||
(doseq [{:keys [address symbol]} contracts]
|
||||
;;TODO(goranjovic): move `get-token-balance` function to wallet models
|
||||
(get-token-balance {:web3 web3
|
||||
:contract address
|
||||
:account-id (:address account)
|
||||
:on-error #(re-frame/dispatch [:update-token-balance-fail symbol %])
|
||||
:on-success #(when (> % 0)
|
||||
(re-frame/dispatch [:wallet/token-found symbol %]))}))))
|
||||
|
||||
(fx/defn wallet-autoconfig-tokens
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [account/account web3 network-status] :wallet/keys [all-tokens]} db
|
||||
network (get (:networks account) (:network account))
|
||||
chain (ethereum/network->chain-keyword network)
|
||||
contracts (->> (tokens/tokens-for all-tokens chain)
|
||||
(remove :hidden?))
|
||||
settings (:settings account)
|
||||
assets (get-in settings [:wallet :visible-tokens chain])]
|
||||
(when-not (or (= network-status :offline)
|
||||
assets)
|
||||
(let [new-settings (assoc-in settings
|
||||
[:wallet :visible-tokens chain]
|
||||
#{})]
|
||||
(fx/merge cofx
|
||||
{:wallet/check-all-known-tokens-balance {:web3 web3
|
||||
:contracts contracts
|
||||
:account account}}
|
||||
(accounts.update/update-settings new-settings {}))))))
|
||||
:wallet/get-tokens-balance
|
||||
(fn [{:keys [wallet-address tokens on-success on-error]}]
|
||||
(doseq [{:keys [address symbol]} tokens]
|
||||
(json-rpc/eth-call
|
||||
{:contract address
|
||||
:method "balanceOf(address)"
|
||||
:params [wallet-address]
|
||||
:outputs ["uint256"]
|
||||
:on-success
|
||||
(fn [[balance]]
|
||||
(on-success symbol (money/bignumber balance)))
|
||||
:on-error #(on-error symbol %)}))))
|
||||
|
||||
(def min-gas-price-wei (money/bignumber 1))
|
||||
|
||||
|
@ -277,7 +252,8 @@
|
|||
(assoc :nonce nonce))))
|
||||
|
||||
;; SEND TRANSACTION -> RPC TRANSACTION
|
||||
(defn prepare-send-transaction [from {:keys [amount to gas gas-price data nonce]}]
|
||||
(defn prepare-send-transaction
|
||||
[from {:keys [amount to gas gas-price data nonce]}]
|
||||
(cond-> {:from (ethereum/normalized-address from)
|
||||
:to (ethereum/normalized-address to)
|
||||
:value (ethereum/int->hex amount)
|
||||
|
@ -288,9 +264,10 @@
|
|||
nonce
|
||||
(assoc :nonce nonce)))
|
||||
|
||||
;; NOTE (andrey) we need this function, because params may be mixed up, so we need to figure out which one is address
|
||||
;; and which message
|
||||
(defn normalize-sign-message-params [params]
|
||||
(defn normalize-sign-message-params
|
||||
"NOTE (andrey) we need this function, because params may be mixed up,
|
||||
so we need to figure out which one is address and which message"
|
||||
[params]
|
||||
(let [first_param (first params)
|
||||
second_param (second params)
|
||||
first-param-address? (ethereum/address? first_param)
|
||||
|
@ -300,14 +277,18 @@
|
|||
[first_param second_param]
|
||||
[second_param first_param]))))
|
||||
|
||||
(defn web3-error-callback [fx {:keys [webview-bridge]} message-id message]
|
||||
(assoc fx :browser/send-to-bridge {:message {:type constants/web3-send-async-callback
|
||||
(defn web3-error-callback
|
||||
[fx {:keys [webview-bridge]} message-id message]
|
||||
(assoc fx :browser/send-to-bridge
|
||||
{:message {:type constants/web3-send-async-callback
|
||||
:messageId message-id
|
||||
:error message}
|
||||
:webview webview-bridge}))
|
||||
|
||||
(defn dapp-complete-transaction [id result method message-id webview keycard?]
|
||||
(cond-> {:browser/send-to-bridge {:message {:type constants/web3-send-async-callback
|
||||
(defn dapp-complete-transaction
|
||||
[id result method message-id webview keycard?]
|
||||
(cond-> {:browser/send-to-bridge
|
||||
{:message {:type constants/web3-send-async-callback
|
||||
:messageId message-id
|
||||
:result {:jsonrpc "2.0"
|
||||
:id (int id)
|
||||
|
@ -316,7 +297,9 @@
|
|||
:dispatch [:navigate-back]}
|
||||
|
||||
(constants/web3-sign-message? method)
|
||||
(assoc :dispatch (if keycard? [:navigate-to :browser] [:navigate-back]))
|
||||
(assoc :dispatch (if keycard?
|
||||
[:navigate-to :browser]
|
||||
[:navigate-back]))
|
||||
|
||||
(= method constants/web3-send-transaction)
|
||||
(assoc :dispatch [:navigate-to-clean :wallet-transaction-sent-modal])))
|
||||
|
@ -350,6 +333,9 @@
|
|||
(fx/defn handle-transaction-error
|
||||
[{:keys [db] :as cofx} {:keys [code message]}]
|
||||
(let [{:keys [on-error]} (get-in db [:wallet :send-transaction])]
|
||||
(log/warn :wallet/transaction-error
|
||||
:code code
|
||||
:message message)
|
||||
(case code
|
||||
;;WRONG PASSWORD
|
||||
constants/send-transaction-err-decrypt
|
||||
|
@ -368,52 +354,87 @@
|
|||
(defn clear-error-message [db error-type]
|
||||
(update-in db [:wallet :errors] dissoc error-type))
|
||||
|
||||
(defn tokens-symbols [visible-token-symbols all-tokens chain]
|
||||
(set/difference (set visible-token-symbols) (set (map :symbol (tokens/nfts-for all-tokens chain)))))
|
||||
(defn tokens-symbols
|
||||
[visible-token-symbols all-tokens chain]
|
||||
(set/difference (set visible-token-symbols)
|
||||
(set (map :symbol (tokens/nfts-for all-tokens chain)))))
|
||||
|
||||
(fx/defn initialize-tokens
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [network-id (get-in db [:account/account :network])
|
||||
network (get-in db [:account/account :networks network-id])
|
||||
custom-tokens (get-in db [:account/account :settings :wallet :custom-tokens])
|
||||
chain (ethereum/network->chain-keyword network)
|
||||
(let [custom-tokens (get-in db [:account/account :settings :wallet :custom-tokens])
|
||||
chain (ethereum/chain-keyword db)
|
||||
all-tokens (merge-with
|
||||
merge
|
||||
(utils.core/map-values #(utils.core/index-by :address %)
|
||||
tokens/all-default-tokens)
|
||||
custom-tokens)]
|
||||
(fx/merge cofx
|
||||
(fx/merge
|
||||
cofx
|
||||
(merge
|
||||
{:db (assoc db :wallet/all-tokens all-tokens)}
|
||||
(when config/erc20-contract-warnings-enabled?
|
||||
{:wallet/validate-tokens {:web3 (:web3 db)
|
||||
:tokens (get tokens/all-default-tokens chain)}}))
|
||||
wallet-autoconfig-tokens)))
|
||||
{:wallet/validate-tokens (get tokens/all-default-tokens chain)})))))
|
||||
|
||||
(fx/defn update-wallet
|
||||
[{{:keys [web3 network network-status]
|
||||
{:keys [address settings]} :account/account :as db} :db}]
|
||||
(let [all-tokens (:wallet/all-tokens db)
|
||||
network (get-in db [:account/account :networks network])
|
||||
chain (ethereum/network->chain-keyword network)
|
||||
(fx/defn update-balances
|
||||
[{{:keys [network-status :wallet/all-tokens]
|
||||
{:keys [address settings]} :account/account :as db} :db :as cofx}]
|
||||
(let [normalized-address (ethereum/normalized-address address)
|
||||
chain (ethereum/chain-keyword db)
|
||||
assets (get-in settings [:wallet :visible-tokens chain])
|
||||
tokens (->> (tokens/tokens-for all-tokens chain)
|
||||
(remove #(or (:hidden? %)))
|
||||
(filter #((or assets identity) (:symbol %))))]
|
||||
(when (not= network-status :offline)
|
||||
(fx/merge
|
||||
cofx
|
||||
{:wallet/get-balance
|
||||
{:account-id normalized-address
|
||||
:on-success #(re-frame/dispatch
|
||||
[:wallet.callback/update-balance-success %])
|
||||
:on-error #(re-frame/dispatch
|
||||
[:wallet.callback/update-balance-fail %])}
|
||||
|
||||
:wallet/get-tokens-balance
|
||||
{:wallet-address normalized-address
|
||||
:tokens tokens
|
||||
:on-success
|
||||
(fn [symbol balance]
|
||||
(if assets
|
||||
(re-frame/dispatch
|
||||
[:wallet.callback/update-token-balance-success symbol balance])
|
||||
;; NOTE: when there is no visible assets set,
|
||||
;; we make an initialization round
|
||||
(when (> balance 0)
|
||||
(re-frame/dispatch
|
||||
[:wallet/token-found symbol balance]))))
|
||||
:on-error
|
||||
(fn [symbol error]
|
||||
(re-frame/dispatch
|
||||
[:wallet.callback/update-token-balance-fail symbol error]))}
|
||||
|
||||
:db
|
||||
(-> db
|
||||
(clear-error-message :balance-update)
|
||||
(assoc-in [:wallet :balance-loading?] true))}
|
||||
(when-not assets
|
||||
(accounts.update/update-settings
|
||||
(assoc-in settings
|
||||
[:wallet :visible-tokens chain]
|
||||
#{})
|
||||
{}))))))
|
||||
|
||||
(fx/defn update-prices
|
||||
[{{:keys [network network-status :wallet/all-tokens]
|
||||
{:keys [address settings networks]} :account/account :as db} :db}]
|
||||
(let [chain (ethereum/chain-keyword db)
|
||||
mainnet? (= :mainnet chain)
|
||||
assets (get-in settings [:wallet :visible-tokens chain])
|
||||
tokens (tokens-symbols (get-in settings [:wallet :visible-tokens chain]) all-tokens chain)
|
||||
tokens (tokens-symbols assets all-tokens chain)
|
||||
currency-id (or (get-in settings [:wallet :currency]) :usd)
|
||||
currency (get constants/currencies currency-id)]
|
||||
(when (not= network-status :offline)
|
||||
{:get-balance {:web3 web3
|
||||
:account-id address
|
||||
:success-event :wallet.callback/update-balance-success
|
||||
:error-event :wallet.callback/update-balance-fail}
|
||||
:get-tokens-balance {:web3 web3
|
||||
:account-id address
|
||||
:symbols assets
|
||||
:chain chain
|
||||
:all-tokens all-tokens
|
||||
:success-event :wallet.callback/update-token-balance-success
|
||||
:error-event :wallet.callback/update-token-balance-fail}
|
||||
:get-prices {:from (if mainnet?
|
||||
{:wallet/get-prices
|
||||
{:from (if mainnet?
|
||||
(conj tokens "ETH")
|
||||
[(-> (tokens/native-currency chain)
|
||||
(wallet.utils/exchange-symbol))])
|
||||
|
@ -422,21 +443,21 @@
|
|||
:success-event :wallet.callback/update-prices-success
|
||||
:error-event :wallet.callback/update-prices-fail
|
||||
:chaos-mode? (:chaos-mode? settings)}
|
||||
:db (-> db
|
||||
|
||||
:db
|
||||
(-> db
|
||||
(clear-error-message :prices-update)
|
||||
(clear-error-message :balance-update)
|
||||
(assoc-in [:wallet :balance-loading?] true)
|
||||
(assoc :prices-loading? true))})))
|
||||
|
||||
(defn open-modal-wallet-for-transaction [db transaction tx-object]
|
||||
(defn open-modal-wallet-for-transaction
|
||||
[db transaction tx-object]
|
||||
(let [{:keys [gas gas-price]} transaction
|
||||
{:keys [wallet-set-up-passed?]} (:account/account db)]
|
||||
{:db (-> db
|
||||
(assoc-in [:navigation/screen-params :wallet-send-modal-stack :modal?] true)
|
||||
(assoc-in [:wallet :send-transaction] transaction)
|
||||
(assoc-in [:wallet :send-transaction :original-gas] gas))
|
||||
:dispatch-n [[:TODO.remove/update-wallet]
|
||||
(when-not gas
|
||||
:dispatch-n [(when-not gas
|
||||
[:TODO.remove/update-estimated-gas tx-object])
|
||||
(when-not gas-price
|
||||
[:wallet/update-gas-price])
|
||||
|
@ -444,9 +465,10 @@
|
|||
(if wallet-set-up-passed?
|
||||
:wallet-send-modal-stack
|
||||
:wallet-send-modal-stack-with-onboarding)]]}))
|
||||
2
|
||||
|
||||
(fx/defn open-sign-transaction-flow
|
||||
[{:keys [db] :as cofx} {:keys [gas gas-price] :as transaction}]
|
||||
[{:keys [db] :as cofx}
|
||||
{:keys [gas gas-price] :as transaction}]
|
||||
(let [go-to-view-id (if (get-in db [:account/account :wallet-set-up-passed?])
|
||||
:wallet-send-modal-stack
|
||||
:wallet-send-modal-stack-with-onboarding)]
|
||||
|
@ -458,17 +480,14 @@
|
|||
(assoc-in [:wallet :send-transaction :original-gas]
|
||||
gas))}
|
||||
(not gas)
|
||||
(assoc :update-estimated-gas
|
||||
{:web3 (:web3 db)
|
||||
:obj (select-keys transaction [:to :data])
|
||||
(assoc :wallet/update-estimated-gas
|
||||
{:obj (select-keys transaction [:to :data])
|
||||
:success-event :wallet/update-estimated-gas-success})
|
||||
|
||||
(not gas-price)
|
||||
(assoc :update-gas-price
|
||||
{:web3 (:web3 db)
|
||||
:success-event :wallet/update-gas-price-success
|
||||
(assoc :wallet/update-gas-price
|
||||
{:success-event :wallet/update-gas-price-success
|
||||
:edit? false}))
|
||||
(update-wallet)
|
||||
(navigation/navigate-to-cofx go-to-view-id {}))))
|
||||
|
||||
(defn send-transaction-screen-did-load
|
||||
|
@ -482,7 +501,7 @@
|
|||
(conj (or ids #{}) id)
|
||||
(disj ids id)))
|
||||
|
||||
(fx/defn update-prices
|
||||
(fx/defn on-update-prices-success
|
||||
[{:keys [db]} prices]
|
||||
{:db (assoc db
|
||||
:prices prices
|
||||
|
@ -491,13 +510,13 @@
|
|||
(fx/defn update-balance
|
||||
[{:keys [db]} balance]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :balance :ETH] balance)
|
||||
(assoc-in [:wallet :balance :ETH] (money/bignumber balance))
|
||||
(assoc-in [:wallet :balance-loading?] false))})
|
||||
|
||||
(fx/defn update-token-balance
|
||||
[{:keys [db]} symbol balance]
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :balance symbol] balance)
|
||||
(assoc-in [:wallet :balance symbol] (money/bignumber balance))
|
||||
(assoc-in [:wallet :balance-loading?] false))})
|
||||
|
||||
(fx/defn update-gas-price
|
||||
|
@ -550,7 +569,8 @@
|
|||
new-settings (update-in settings [:wallet :custom-tokens chain] dissoc address)]
|
||||
(accounts.update/update-settings cofx new-settings {})))
|
||||
|
||||
(fx/defn configure-token-balance-and-visibility [cofx symbol balance]
|
||||
(fx/defn configure-token-balance-and-visibility
|
||||
[cofx symbol balance]
|
||||
(fx/merge cofx
|
||||
(toggle-visible-token symbol true)
|
||||
;;TODO(goranjovic): move `update-token-balance-success` function to wallet models
|
||||
|
|
|
@ -39,13 +39,6 @@
|
|||
(fn [error sync]
|
||||
(re-frame/dispatch [:web3.callback/get-syncing-success error sync])))))
|
||||
|
||||
(defn get-block-number-fx [web3]
|
||||
(when web3
|
||||
(.getBlockNumber
|
||||
(.-eth web3)
|
||||
(fn [error block-number]
|
||||
(re-frame/dispatch [:web3.callback/get-block-number error block-number])))))
|
||||
|
||||
(defn set-default-account
|
||||
[web3 address]
|
||||
(set! (.-defaultAccount (.-eth web3))
|
||||
|
|
|
@ -13,10 +13,6 @@
|
|||
:web3/get-syncing
|
||||
web3/get-syncing)
|
||||
|
||||
(re-frame/reg-fx
|
||||
:web3/get-block-number
|
||||
web3/get-block-number-fx)
|
||||
|
||||
(re-frame/reg-fx
|
||||
:web3/set-default-account
|
||||
(fn [[web3 address]]
|
||||
|
|
|
@ -199,12 +199,11 @@
|
|||
(set (:utils/dispatch-later efx)))))
|
||||
(testing "Check the rest of effects."
|
||||
(is (contains? efx :web3/set-default-account))
|
||||
(is (contains? efx :web3/get-block-number))
|
||||
(is (contains? efx :web3/fetch-node-version))
|
||||
(is (contains? efx :get-balance))
|
||||
(is (contains? efx :web3/get-syncing))
|
||||
(is (contains? efx :get-tokens-balance))
|
||||
(is (contains? efx :get-prices))))))
|
||||
(is (contains? efx :wallet/get-balance))
|
||||
(is (contains? efx :wallet/get-tokens-balance))
|
||||
(is (contains? efx :wallet/get-prices))))))
|
||||
|
||||
(deftest login-failed
|
||||
(testing
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
(deftest set-currency
|
||||
(let [cofx (models/set-currency {:db {:account/account {:settings {:wallet {}}}}} :usd)]
|
||||
(is (= [:db :get-balance :get-tokens-balance :get-prices :data-store/base-tx] (keys cofx)))
|
||||
(is (= [:db :wallet/get-prices :data-store/base-tx] (keys cofx)))
|
||||
(is (= :usd (get-in cofx [:db :account/account :settings :wallet :currency]))))
|
||||
(is (= :jpy (get-in (models/set-currency {:db {:account/account {:settings {:wallet {}}}}} :jpy)
|
||||
[:db :account/account :settings :wallet :currency]))))
|
||||
|
|
Loading…
Reference in New Issue