[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:
yenda 2019-05-19 21:47:54 +02:00
parent 89680f4861
commit fc4c772c0b
No known key found for this signature in database
GPG Key ID: 0095623C0069DCE6
24 changed files with 492 additions and 708 deletions

View File

@ -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)]

View File

@ -333,9 +333,9 @@
;; 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
:edit? false}}
:wallet/update-gas-price
{:success-event :wallet/update-gas-price-success
:edit? false}}
(navigation/navigate-to-cofx next-view-id {}))))
protocol/EnhancedParameters
(enhance-send-parameters [_ parameters cofx]

View File

@ -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)))}}))))

View File

@ -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}))

View File

@ -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"
:toBlock "latest"
:topics [constants/event-transfer-hash from to]}]
(transactions/inbound-token-transfer-handler chain-tokens)))
(json-rpc/call
{:method "eth_newFilter"
:params [{:fromBlock "latest"
:toBlock "latest"
:topics [constants/event-transfer-hash from to]}]
: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" []
(fn [[block-hash]]
(transactions/get-block-by-hash
block-hash
(fn [block]
(re-frame/dispatch [:ethereum.signal/new-block block]))))))
(json-rpc/call
{:method "eth_newBlockFilter"
:on-success
(fn [[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/reg-fx
:ethereum.subscriptions/new-block

View File

@ -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,31 +50,33 @@
[chain-tokens
{:keys [number timestamp]}
{:keys [transfer direction hash gasPrice value gas from input nonce to] :as transaction}]
(get-transaction-receipt
hash
(fn [{:keys [gasUsed logs] :as receipt}]
(let [[event _ _] (:topics (first logs))
transfer (= constants/event-transfer-hash event)]
(re-frame/dispatch
[:ethereum.transactions/new
(merge {:block (str number)
:timestamp (str (* timestamp 1000))
:gas-used (str (decode/uint gasUsed))
:gas-price (str (decode/uint gasPrice))
:gas-limit (str (decode/uint gas))
:nonce (str (decode/uint nonce))
:data input}
(if transfer
(parse-token-transfer chain-tokens
:outbound
(first logs))
;; this is not a ERC20 token transaction
{:hash hash
:symbol :ETH
:from from
:to to
:type direction
:value (str (decode/uint value))}))])))))
(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)]
(re-frame/dispatch
[:ethereum.transactions/new
(merge {:block (str number)
:timestamp (str (* timestamp 1000))
:gas-used (str (decode/uint gasUsed))
:gas-price (str (decode/uint gasPrice))
:gas-limit (str (decode/uint gas))
:nonce (str (decode/uint nonce))
:data input}
(if transfer
(parse-token-transfer chain-tokens
:outbound
(first logs))
;; this is not a ERC20 token transaction
{:hash hash
:symbol :ETH
:from from
:to to
:type direction
:value (str (decode/uint value))}))])))}))
(re-frame/reg-fx
:ethereum.transactions/enrich-transactions-from-new-blocks
@ -153,28 +109,34 @@
;; 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
(fn [{:keys [timestamp number]}]
(let [timestamp (str (* timestamp 1000))]
(doseq [{:keys [hash] :as transfer} block-transfers]
(get-transaction-by-hash
hash
(fn [{:keys [gasPrice gas input nonce]}]
(get-transaction-receipt
hash
(fn [{:keys [gasUsed]}]
(re-frame/dispatch
[:ethereum.transactions/new
(-> transfer
(dissoc :block-hash)
(assoc :timestamp timestamp
:block (str number)
:gas-used (str (decode/uint gasUsed))
:gas-price (str (decode/uint gasPrice))
:gas-limit (str (decode/uint gas))
:data input
:nonce (str (decode/uint nonce))))])))))))))))))
(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]
(json-rpc/call
{:method "eth_getTransactionByHash"
:params [hash]
:on-success
(fn [{:keys [gasPrice gas input nonce]}]
(json-rpc/call
{:method "eth_getTransactionReceipt"
:params [hash]
:on-success
(fn [{:keys [gasUsed]}]
(re-frame/dispatch
[:ethereum.transactions/new
(-> transfer
(dissoc :block-hash)
(assoc :timestamp timestamp
:block (str number)
:gas-used (str (decode/uint gasUsed))
:gas-price (str (decode/uint gasPrice))
:gas-limit (str (decode/uint gas))
:data input
: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]

View File

@ -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,9 +2232,9 @@
(handlers/register-handler-fx
:TODO.remove/update-estimated-gas
(fn [{:keys [db]} [_ obj]]
{:update-estimated-gas {:web3 (:web3 db)
:obj obj
:success-event :wallet/update-estimated-gas-success}}))
{:wallet/update-estimated-gas
{:obj obj
:success-event :wallet/update-estimated-gas-success}}))
(handlers/register-handler-fx
:wallet/update-estimated-gas-success

View File

@ -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

View File

@ -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?)))

View File

@ -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

View File

@ -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)]

View File

@ -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]))

View File

@ -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))))

View File

@ -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

View File

@ -51,9 +51,9 @@
;; 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
:edit? false}})
(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)
(assoc-in [:db :wallet :send-transaction :asset-error]

View File

@ -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,9 +270,9 @@
(defn update-gas-price
([db edit? success-event]
{:update-gas-price {:web3 (:web3 db)
:success-event (or success-event :wallet/update-gas-price-success)
:edit? edit?}})
{: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)))

View File

@ -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)))}})))))

View File

@ -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)

View File

@ -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)))))))

View File

@ -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))
(let [message (i18n/label :t/token-auto-validate-name-error
{:symbol symbol
:expected name
:actual %2
:address address})]
(log/warn message)
(utils.utils/show-popup (i18n/label :t/warning) message)))))
(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 contract-name
:address address})]
(log/warn 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))
(let [message (i18n/label :t/token-auto-validate-symbol-error
{:symbol symbol
:expected (clojure.core/name symbol)
:actual %2
:address address})]
(log/warn message)
(utils.utils/show-popup (i18n/label :t/warning) message)))))
(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 contract-symbol
:address address})]
(log/warn 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)))
(let [message (i18n/label :t/token-auto-validate-decimals-error
{:symbol symbol
:expected decimals
:actual %2
:address address})]
(log/warn message)
(utils.utils/show-popup (i18n/label :t/warning) message))))))
(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 contract-decimals
:address address})]
(log/warn 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,23 +277,29 @@
[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
:messageId message-id
:error message}
:webview webview-bridge}))
(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
:messageId message-id
:result {:jsonrpc "2.0"
:id (int id)
:result result}}
:webview webview}
:dispatch [:navigate-back]}
(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)
:result result}}
:webview webview}
: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,75 +354,110 @@
(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
(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)))
(fx/merge
cofx
(merge
{:db (assoc db :wallet/all-tokens all-tokens)}
(when config/erc20-contract-warnings-enabled?
{: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?
(conj tokens "ETH")
[(-> (tokens/native-currency chain)
(wallet.utils/exchange-symbol))])
:to [(:code currency)]
:mainnet? mainnet?
:success-event :wallet.callback/update-prices-success
:error-event :wallet.callback/update-prices-fail
:chaos-mode? (:chaos-mode? settings)}
:db (-> db
(clear-error-message :prices-update)
(clear-error-message :balance-update)
(assoc-in [:wallet :balance-loading?] true)
(assoc :prices-loading? true))})))
{:wallet/get-prices
{:from (if mainnet?
(conj tokens "ETH")
[(-> (tokens/native-currency chain)
(wallet.utils/exchange-symbol))])
:to [(:code currency)]
:mainnet? mainnet?
:success-event :wallet.callback/update-prices-success
:error-event :wallet.callback/update-prices-fail
:chaos-mode? (:chaos-mode? settings)}
(defn open-modal-wallet-for-transaction [db transaction tx-object]
:db
(-> db
(clear-error-message :prices-update)
(assoc :prices-loading? true))})))
(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

View File

@ -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))

View File

@ -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]]

View File

@ -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

View File

@ -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]))))