[refactor] remove ethereum `call` and `call-params`
- use `json-rpc/eth-call` and `json-rpc/eth-transaction-call` everywhere - move all conversions to abi-spec
This commit is contained in:
parent
fc4c772c0b
commit
b6fecd4e1c
|
@ -1,61 +1,17 @@
|
|||
(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]))
|
||||
(:require [status-im.utils.ethereum.core :as ethereum]))
|
||||
|
||||
(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}}}})
|
||||
{:status/snt
|
||||
{:mainnet "0x744d70fdbe2ba4cf95131626614a1763df805b9e"
|
||||
:testnet "0xc55cf4b03948d7ebc8b9e8bad92643703811d162"}
|
||||
:status/tribute-to-talk
|
||||
{:testnet "0x3da3fc53e24707f36c5b4433b442e896c4955f0e"}
|
||||
:status/stickers
|
||||
{:testnet "0x39d16CdB56b5a6a89e1A397A13Fe48034694316E"}})
|
||||
|
||||
(re-frame/reg-fx
|
||||
::call
|
||||
(fn [{:keys [address data callback]}]
|
||||
(ethereum/call {:to address
|
||||
:data data}
|
||||
callback)))
|
||||
|
||||
(defn get-contract-address
|
||||
(defn get-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)))}}))))
|
||||
(get-in contracts [contract chain-keyword])))
|
||||
|
|
|
@ -3,22 +3,94 @@
|
|||
[re-frame.core :as re-frame]
|
||||
[status-im.accounts.core :as accounts]
|
||||
[status-im.constants :as constants]
|
||||
[status-im.ethereum.contracts :as contracts]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[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.stickers :as ethereum.stickers]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.utils.multihash :as multihash]
|
||||
[status-im.utils.utils :as utils]
|
||||
[status-im.wallet.core :as wallet]))
|
||||
|
||||
(fx/defn init-stickers-packs [{:keys [db]}]
|
||||
(defn pack-data-callback
|
||||
[id open?]
|
||||
(fn [[category owner mintable timestamp price contenthash]]
|
||||
(let [proto-code (subs contenthash 2 4)
|
||||
hash (when contenthash
|
||||
(multihash/base58 (multihash/create :sha2-256 (subs contenthash 12))))]
|
||||
(when (and (#{constants/swarm-proto-code constants/ipfs-proto-code}
|
||||
proto-code) hash)
|
||||
(re-frame/dispatch [:stickers/load-pack proto-code hash id price open?])))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:stickers/set-pending-timout-fx
|
||||
(fn []
|
||||
(utils/set-timeout #(re-frame/dispatch [:stickers/pending-timout])
|
||||
10000)))
|
||||
|
||||
(defn eth-call-pack-data
|
||||
[contract id open?]
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
;; Returns vector of pack data parameters by pack id:
|
||||
;; [category owner mintable timestamp price contenthash]
|
||||
:method "getPackData(uint256)"
|
||||
:params [id]
|
||||
:outputs ["bytes4[]" "address" "bool" "uint256" "uint256" "bytes"]
|
||||
:on-success (pack-data-callback id open?)}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:stickers/pack-data-fx
|
||||
(fn [[contract id]]
|
||||
(eth-call-pack-data contract id true)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:stickers/load-packs-fx
|
||||
(fn [[contract]]
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
;; Returns number of packs registered in the contract
|
||||
:method "packCount()"
|
||||
:outputs ["uint256"]
|
||||
:on-success
|
||||
(fn [[count]]
|
||||
(dotimes [id count]
|
||||
(eth-call-pack-data contract id false)))})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:stickers/owned-packs-fx
|
||||
(fn [[contract address]]
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
;; Returns vector of owned tokens ids in the contract by address
|
||||
:method "tokensOwnedBy(address)"
|
||||
:params [address]
|
||||
:outputs ["uint256[]"]
|
||||
:on-success
|
||||
(fn [[tokens]]
|
||||
(doseq [id tokens]
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
;; Returns pack id in the contract by token id
|
||||
:method "tokenPackId(uint256)"
|
||||
:params [id]
|
||||
:outputs ["uint256"]
|
||||
:on-success
|
||||
(fn [[pack-id]]
|
||||
(re-frame/dispatch [:stickers/pack-owned pack-id]))})))})))
|
||||
|
||||
(fx/defn init-stickers-packs
|
||||
[{:keys [db]}]
|
||||
(let [sticker-packs (into {} (map #(let [pack (edn/read-string %)]
|
||||
(vector (:id pack) pack))
|
||||
(get-in db [:account/account :stickers])))]
|
||||
{:db (assoc db :stickers/packs-installed sticker-packs :stickers/packs sticker-packs)}))
|
||||
{:db (assoc db
|
||||
:stickers/packs-installed sticker-packs
|
||||
:stickers/packs sticker-packs)}))
|
||||
|
||||
(fx/defn install-stickers-pack [{{:account/keys [account] :as db} :db :as cofx} id]
|
||||
(fx/defn install-stickers-pack
|
||||
[{{:account/keys [account] :as db} :db :as cofx} id]
|
||||
(let [pack (get-in db [:stickers/packs id])]
|
||||
(fx/merge
|
||||
cofx
|
||||
|
@ -27,125 +99,91 @@
|
|||
(assoc :stickers/selected-pack id))}
|
||||
(accounts/update-stickers (conj (:stickers account) (pr-str pack))))))
|
||||
|
||||
(fx/defn load-sticker-pack-success [{:keys [db] :as cofx} edn-string id price open?]
|
||||
(fx/defn load-sticker-pack-success
|
||||
[{:keys [db] :as cofx} edn-string id price open?]
|
||||
(let [pack (assoc (get (edn/read-string edn-string) 'meta)
|
||||
:id id :price price)]
|
||||
(fx/merge cofx
|
||||
{:db (-> db (assoc-in [:stickers/packs id] pack))}
|
||||
#(when open? (navigation/navigate-to-cofx % :stickers-pack-modal pack)))))
|
||||
|
||||
(defn pack-data-callback [id open?]
|
||||
(fn [[category owner mintable timestamp price contenthash]]
|
||||
(let [proto-code (subs contenthash 2 4)
|
||||
hash (when contenthash (multihash/base58 (multihash/create :sha2-256 (subs contenthash 12))))]
|
||||
(when (and (#{constants/swarm-proto-code constants/ipfs-proto-code} proto-code) hash)
|
||||
(re-frame/dispatch [:stickers/load-pack proto-code hash id price open?])))))
|
||||
{:db (assoc-in db [:stickers/packs id] pack)}
|
||||
#(when open?
|
||||
(navigation/navigate-to-cofx % :stickers-pack-modal pack)))))
|
||||
|
||||
(fx/defn open-sticker-pack
|
||||
[{{:keys [network] :stickers/keys [packs packs-installed] :as db} :db :as cofx} id]
|
||||
[{{:stickers/keys [packs packs-installed] :as db} :db :as cofx} id]
|
||||
(when id
|
||||
(let [pack (or (get packs-installed id) (get packs id))
|
||||
network (get-in db [:account/account :networks network])]
|
||||
(let [pack (or (get packs-installed id)
|
||||
(get packs id))
|
||||
contract-address (contracts/get-address db :status/stickers)]
|
||||
(if pack
|
||||
(navigation/navigate-to-cofx cofx :stickers-pack-modal pack)
|
||||
{:stickers/pack-data-fx [network id true]}))))
|
||||
(when contract-address
|
||||
{:stickers/pack-data-fx [contract-address id]})))))
|
||||
|
||||
(fx/defn load-pack [cofx proto-code hash id price open?]
|
||||
{:http-get {:url (str (if (= constants/swarm-proto-code proto-code)
|
||||
"https://swarm-gateways.net/bzz:/"
|
||||
"https://ipfs.infura.io/ipfs/")
|
||||
hash)
|
||||
:success-event-creator (fn [o]
|
||||
[:stickers/load-sticker-pack-success o id price open?])
|
||||
:failure-event-creator (constantly nil)}})
|
||||
(fx/defn load-pack
|
||||
[cofx proto-code hash id price open?]
|
||||
{:http-get {:url (str (if (= constants/swarm-proto-code proto-code)
|
||||
"https://swarm-gateways.net/bzz:/"
|
||||
"https://ipfs.infura.io/ipfs/")
|
||||
hash)
|
||||
:success-event-creator
|
||||
(fn [o]
|
||||
[:stickers/load-sticker-pack-success o id price open?])
|
||||
:failure-event-creator
|
||||
(constantly nil)}})
|
||||
|
||||
(fx/defn load-packs [{{:keys [network] :as db} :db}]
|
||||
(let [network (get-in db [:account/account :networks network])
|
||||
address (ethereum/normalized-address (get-in db [:account/account :address]))]
|
||||
{:stickers/owned-packs-fx [network address]
|
||||
:stickers/load-packs-fx [network]}))
|
||||
(fx/defn load-packs
|
||||
[{:keys [db]}]
|
||||
(let [contract (contracts/get-address db :status/stickers)
|
||||
address (ethereum/current-address db)]
|
||||
(when contract
|
||||
{:stickers/owned-packs-fx [contract address]
|
||||
:stickers/load-packs-fx [contract]})))
|
||||
|
||||
(defn prepare-transaction [id tx on-result]
|
||||
(merge {:id id
|
||||
:symbol :ETH
|
||||
:method constants/web3-send-transaction
|
||||
:amount (money/bignumber 0)}
|
||||
(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 snt-contracts chain)
|
||||
:data (abi-spec/encode "approveAndCall(address,uint256,bytes)" [stickers-contract price data])}]
|
||||
(wallet/open-modal-wallet-for-transaction
|
||||
db
|
||||
(prepare-transaction "approve" tx-object [:stickers/pending-pack pack-id])
|
||||
tx-object)))
|
||||
(fx/defn approve-pack
|
||||
[{db :db :as cofx} pack-id price]
|
||||
(let [address (ethereum/current-address db)
|
||||
chain (ethereum/chain-keyword db)
|
||||
stickers-contract (contracts/get-address db :status/stickers)
|
||||
snt-contract (contracts/get-address db :status/snt)]
|
||||
(wallet/eth-transaction-call
|
||||
cofx
|
||||
{:contract snt-contract
|
||||
:method "approveAndCall(address,uint256,bytes)"
|
||||
:params [stickers-contract
|
||||
price
|
||||
(abi-spec/encode "buyToken(uint256,address)"
|
||||
[pack-id address])]
|
||||
:on-result [:stickers/pending-pack pack-id]})))
|
||||
|
||||
(fx/defn pending-pack
|
||||
[{{:keys [network] :as db} :db :as cofx} id]
|
||||
(let [network (get-in db [:account/account :networks network])
|
||||
address (ethereum/normalized-address (get-in db [:account/account :address]))]
|
||||
(fx/merge cofx
|
||||
{:db (update db :stickers/packs-pendning conj id)
|
||||
:stickers/owned-packs-fx [network address]}
|
||||
(navigation/navigate-to-clean :wallet-transaction-sent-modal {})
|
||||
#(when (zero? (count (:stickers/packs-pendning db)))
|
||||
{:stickers/set-pending-timout-fx nil}))))
|
||||
[{:keys [db] :as cofx} id]
|
||||
(let [contract (contracts/get-address db :status/stickers)
|
||||
address (ethereum/current-address db)]
|
||||
(when contract
|
||||
(fx/merge cofx
|
||||
{:db (update db :stickers/packs-pending conj id)
|
||||
:stickers/owned-packs-fx [contract address]}
|
||||
(navigation/navigate-to-clean :wallet-transaction-sent-modal {})
|
||||
#(when (zero? (count (:stickers/packs-pending db)))
|
||||
{:stickers/set-pending-timout-fx nil})))))
|
||||
|
||||
(fx/defn pending-timeout
|
||||
[{{:keys [network] :stickers/keys [packs-pendning packs-owned] :as db} :db}]
|
||||
(let [packs-diff (clojure.set/difference packs-pendning packs-owned)
|
||||
network (get-in db [:account/account :networks network])
|
||||
address (ethereum/normalized-address (get-in db [:account/account :address]))]
|
||||
(merge {:db (assoc db :stickers/packs-pendning packs-diff)}
|
||||
(when-not (zero? (count packs-diff))
|
||||
{:stickers/owned-packs-fx [network address]
|
||||
:stickers/set-pending-timout-fx nil}))))
|
||||
[{{:stickers/keys [packs-pending packs-owned] :as db} :db}]
|
||||
(let [packs-diff (clojure.set/difference packs-pending packs-owned)
|
||||
contract (contracts/get-address db :status/stickers)
|
||||
address (ethereum/current-address db)]
|
||||
(when contract
|
||||
(merge {:db (assoc db :stickers/packs-pending packs-diff)}
|
||||
(when-not (zero? (count packs-diff))
|
||||
{:stickers/owned-packs-fx [contract address]
|
||||
:stickers/set-pending-timout-fx nil})))))
|
||||
|
||||
(fx/defn pack-owned [{db :db} id]
|
||||
{:db (update db :stickers/packs-owned conj id)})
|
||||
|
||||
(fx/defn get-owned-pack
|
||||
[{{:keys [network] :as db} :db}]
|
||||
(let [address (ethereum/normalized-address (get-in db [:account/account :address]))]
|
||||
{:stickers/owned-packs-fx [network address]}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:stickers/pack-data-fx
|
||||
(fn [[network id open?]]
|
||||
(when-let [contract (get ethereum.stickers/contracts (ethereum/network->chain-keyword network))]
|
||||
(ethereum.stickers/pack-data contract id (pack-data-callback id open?)))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:stickers/set-pending-timout-fx
|
||||
(fn []
|
||||
(js/setTimeout #(re-frame/dispatch [:stickers/pending-timout]) 10000)))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:stickers/load-packs-fx
|
||||
(fn [[network]]
|
||||
(when-let [contract (get ethereum.stickers/contracts (ethereum/network->chain-keyword network))]
|
||||
(ethereum.stickers/pack-count contract
|
||||
(fn [count]
|
||||
(dotimes [n count]
|
||||
(ethereum.stickers/pack-data contract n (pack-data-callback n false))))))))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:stickers/owned-packs-fx
|
||||
(fn [[network address]]
|
||||
(when-let [contract (get ethereum.stickers/contracts (ethereum/network->chain-keyword network))]
|
||||
(ethereum.stickers/owned-tokens contract address
|
||||
(fn [tokens]
|
||||
(doseq [n tokens]
|
||||
(ethereum.stickers/token-pack-id contract n
|
||||
#(re-frame/dispatch [:stickers/pack-owned %]))))))))
|
||||
[{:keys [db]}]
|
||||
(let [contract (contracts/get-address db :status/stickers)
|
||||
address (ethereum/current-address db)]
|
||||
(when contract
|
||||
{:stickers/owned-packs-fx [contract address]})))
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
(reg-root-key-sub :stickers/packs :stickers/packs)
|
||||
(reg-root-key-sub :stickers/installed-packs :stickers/packs-installed)
|
||||
(reg-root-key-sub :stickers/packs-owned :stickers/packs-owned)
|
||||
(reg-root-key-sub :stickers/packs-pendning :stickers/packs-pendning)
|
||||
(reg-root-key-sub :stickers/packs-pending :stickers/packs-pending)
|
||||
|
||||
;;mailserver
|
||||
(reg-root-key-sub :mailserver/current-id :mailserver/current-id)
|
||||
|
@ -768,7 +768,7 @@
|
|||
:<- [:stickers/packs]
|
||||
:<- [:stickers/installed-packs]
|
||||
:<- [:stickers/packs-owned]
|
||||
:<- [:stickers/packs-pendning]
|
||||
:<- [:stickers/packs-pending]
|
||||
(fn [[packs installed owned pending]]
|
||||
(map (fn [{:keys [id] :as pack}]
|
||||
(cond-> pack
|
||||
|
@ -1167,9 +1167,8 @@
|
|||
(let [{:keys [gas-used gas-price hash timestamp type token value]
|
||||
:as transaction}
|
||||
(get transactions current-transaction)
|
||||
native-currency-text (-> native-currency
|
||||
:symbol-display
|
||||
name)]
|
||||
native-currency-text (name (or (:symbol-display native-currency)
|
||||
(:symbol native-currency)))]
|
||||
(when transaction
|
||||
(merge transaction
|
||||
{:gas-price-eth (if gas-price
|
||||
|
|
|
@ -5,12 +5,14 @@
|
|||
[status-im.accounts.update.core :as accounts.update]
|
||||
[status-im.contact.db :as contact.db]
|
||||
[status-im.ethereum.contracts :as contracts]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[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.core :as ethereum]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.wallet.core :as wallet]
|
||||
[taoensso.timbre :as log]))
|
||||
|
||||
(fx/defn update-settings
|
||||
|
@ -195,25 +197,35 @@
|
|||
[:tribute-to-talk.callback/fetch-manifest-success
|
||||
identity manifest])))}))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:tribute-to-talk/get-manifest
|
||||
(fn [{:keys [contract address on-success]}]
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
:method "getManifest(address)"
|
||||
:params [address]
|
||||
:outputs ["bytes"]
|
||||
:on-success on-success})))
|
||||
|
||||
(fx/defn check-manifest
|
||||
[{:keys [db] :as cofx} identity]
|
||||
(or (contracts/call cofx
|
||||
{:contract :status/tribute-to-talk
|
||||
:method :get-manifest
|
||||
:params [(contact.db/public-key->address identity)]
|
||||
:return-params ["bytes"]
|
||||
:callback
|
||||
#(re-frame/dispatch
|
||||
(if-let [contenthash (first %)]
|
||||
[:tribute-to-talk.callback/check-manifest-success
|
||||
identity
|
||||
contenthash]
|
||||
[:tribute-to-talk.callback/no-manifest-found identity]))})
|
||||
;; `contracts/call` returns nil if there is no contract for the current network
|
||||
;; update settings if checking own manifest or do nothing otherwise
|
||||
(when-let [me? (= identity
|
||||
(get-in cofx [:db :account/account :public-key]))]
|
||||
(update-settings cofx nil))))
|
||||
[{:keys [db] :as cofx} public-key]
|
||||
(if-let [contract (contracts/get-address db :status/tribute-to-talk)]
|
||||
(let [address (contact.db/public-key->address public-key)]
|
||||
{:tribute-to-talk/get-manifest
|
||||
{:contract contract
|
||||
:address address
|
||||
:on-success
|
||||
(fn [[contenthash]]
|
||||
(re-frame/dispatch
|
||||
(if contenthash
|
||||
[:tribute-to-talk.callback/check-manifest-success
|
||||
public-key
|
||||
contenthash]
|
||||
[:tribute-to-talk.callback/no-manifest-found public-key])))}})
|
||||
;; update settings if checking own manifest or do nothing otherwise
|
||||
(when-let [me? (= identity
|
||||
(get-in cofx [:db :account/account :public-key]))]
|
||||
(update-settings cofx nil))))
|
||||
|
||||
(fx/defn check-own-manifest
|
||||
[cofx]
|
||||
|
@ -224,13 +236,17 @@
|
|||
(let [contenthash (when hash
|
||||
(contenthash/encode {:hash hash
|
||||
:namespace :ipfs}))]
|
||||
(or (contracts/call cofx
|
||||
{:contract :status/tribute-to-talk
|
||||
:method :set-manifest
|
||||
:params [contenthash]
|
||||
:on-result [:tribute-to-talk.callback/set-manifest-transaction-completed]
|
||||
:on-error [:tribute-to-talk.callback/set-manifest-transaction-failed]})
|
||||
{:db (assoc-in db [:navigation/screen-params :tribute-to-talk :state] :transaction-failed)})))
|
||||
(if-let [contract (contracts/get-address db :status/tribute-to-talk)]
|
||||
(wallet/eth-transaction-call
|
||||
cofx
|
||||
{:contract contract
|
||||
:method "setManifest(bytes)"
|
||||
:params [contenthash]
|
||||
:on-result [:tribute-to-talk.callback/set-manifest-transaction-completed]
|
||||
:on-error [:tribute-to-talk.callback/set-manifest-transaction-failed]})
|
||||
{:db (assoc-in db
|
||||
[:navigation/screen-params :tribute-to-talk :state]
|
||||
:transaction-failed)})))
|
||||
|
||||
(defn remove
|
||||
[{:keys [db] :as cofx}]
|
||||
|
|
|
@ -14,15 +14,14 @@
|
|||
|
||||
(handlers/register-handler-fx
|
||||
:new-chat/set-new-identity
|
||||
(fn [{{:keys [network network-status] :as db} :db} [_ new-identity]]
|
||||
(fn [{{:keys [network-status] :as db} :db} [_ new-identity]]
|
||||
(let [is-public-key? (and (string? new-identity)
|
||||
(string/starts-with? new-identity "0x"))]
|
||||
(merge {:db (assoc db
|
||||
:contacts/new-identity new-identity
|
||||
:contacts/new-identity-error (db/validate-pub-key db new-identity))}
|
||||
(when-not is-public-key?
|
||||
(let [network (get-in db [:account/account :networks network])
|
||||
chain (ethereum/network->chain-keyword network)]
|
||||
(let [chain (ethereum/chain-keyword db)]
|
||||
{:resolve-public-key {:registry (get ens/ens-registries chain)
|
||||
:ens-name (if (ens/is-valid-eth-name? new-identity)
|
||||
new-identity
|
||||
|
|
|
@ -93,7 +93,8 @@
|
|||
[stickers-panel (map #(assoc % :pack id) stickers) window-width])]))
|
||||
|
||||
(defn pack-icon [{:keys [id on-press background-color]
|
||||
:or {on-press #(re-frame/dispatch [:stickers/select-pack id])}} icon]
|
||||
:or {on-press #(re-frame/dispatch [:stickers/select-pack id])}}
|
||||
icon]
|
||||
[react/touchable-highlight {:on-press on-press}
|
||||
[react/view {:style {:align-items :center}}
|
||||
[react/view {:style (styles/pack-icon background-color icon-size icon-horizontal-margin)}
|
||||
|
@ -148,7 +149,8 @@
|
|||
[vector-icons/icon :stickers-icons/recent {:color colors/gray}]]
|
||||
(for [{:keys [id thumbnail]} installed-packs]
|
||||
^{:key id}
|
||||
[pack-icon {:id id}
|
||||
[pack-icon {:id id
|
||||
:background-color colors/white}
|
||||
[react/image {:style {:width icon-size :height icon-size :border-radius (/ icon-size 2)}
|
||||
:source {:uri thumbnail}}]])]
|
||||
[scroll-indicator]]]]]))
|
||||
|
|
|
@ -61,7 +61,7 @@
|
|||
:push-notifications/stored {}
|
||||
:registry {}
|
||||
:stickers/packs-owned #{}
|
||||
:stickers/packs-pendning #{}
|
||||
:stickers/packs-pending #{}
|
||||
:hardwallet {:nfc-supported? false
|
||||
:nfc-enabled? false
|
||||
:pin {:original []
|
||||
|
@ -192,7 +192,7 @@
|
|||
|
||||
(spec/def :stickers/packs (spec/nilable map?))
|
||||
(spec/def :stickers/packs-owned (spec/nilable set?))
|
||||
(spec/def :stickers/packs-pendning (spec/nilable set?))
|
||||
(spec/def :stickers/packs-pending (spec/nilable set?))
|
||||
(spec/def :stickers/packs-installed (spec/nilable map?))
|
||||
(spec/def :stickers/selected-pack (spec/nilable any?))
|
||||
(spec/def :stickers/recent (spec/nilable vector?))
|
||||
|
@ -269,7 +269,7 @@
|
|||
:stickers/selected-pack
|
||||
:stickers/recent
|
||||
:stickers/packs-owned
|
||||
:stickers/packs-pendning
|
||||
:stickers/packs-pending
|
||||
:bottom-sheet/show?
|
||||
:bottom-sheet/view
|
||||
:bottom-sheet/options
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
[status-im.native-module.core :as status]
|
||||
[status-im.transport.utils :as transport.utils]
|
||||
[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.tokens :as tokens]
|
||||
[status-im.utils.fx :as fx]
|
||||
|
@ -29,10 +30,13 @@
|
|||
{:keys [from to value gas gasPrice]} on-completed masked-password]
|
||||
(let [contract (:address (tokens/symbol->token all-tokens (keyword chain) symbol))]
|
||||
(status/send-transaction (types/clj->json
|
||||
(merge (ethereum/call-params contract "transfer(address,uint256)" to value)
|
||||
{:from from
|
||||
:gas gas
|
||||
:gasPrice gasPrice}))
|
||||
{:to contract
|
||||
:from from
|
||||
:data (abi-spec/encode
|
||||
"transfer(address,uint256)"
|
||||
[to value])
|
||||
:gas gas
|
||||
:gasPrice gasPrice})
|
||||
(security/safe-unmask-data masked-password)
|
||||
on-completed)))
|
||||
|
||||
|
@ -66,9 +70,10 @@
|
|||
(handlers/register-handler-fx
|
||||
:wallet/send-transaction
|
||||
(fn [{{:keys [chain] :as db} :db} _]
|
||||
(let [{:keys [password symbol in-progress?] :as transaction} (get-in db [:wallet :send-transaction])
|
||||
(let [{:keys [password symbol in-progress?] :as transaction}
|
||||
(get-in db [:wallet :send-transaction])
|
||||
all-tokens (:wallet/all-tokens db)
|
||||
from (get-in db [:account/account :address])]
|
||||
from (ethereum/current-address db)]
|
||||
(when-not in-progress?
|
||||
{:db (-> db
|
||||
(assoc-in [:wallet :send-transaction :wrong-password?] false)
|
||||
|
@ -374,8 +379,9 @@
|
|||
(wallet/prepare-send-transaction from transaction)
|
||||
(let [contract (:address (tokens/symbol->token all-tokens (keyword chain) symbol))
|
||||
{:keys [gas gasPrice to from value]} (wallet/prepare-send-transaction from transaction)]
|
||||
(merge (ethereum/call-params contract "transfer(address,uint256)" to value)
|
||||
{:from from
|
||||
(merge (abi-spec/encode "transfer(address,uint256)" [to value])
|
||||
{:to contract
|
||||
:from from
|
||||
:gas gas
|
||||
:gasPrice gasPrice}))))
|
||||
|
||||
|
|
|
@ -28,6 +28,9 @@
|
|||
(when x
|
||||
(subs (.fromUtf8 utils x) 2)))
|
||||
|
||||
(defn hex-to-boolean [x]
|
||||
(= x "0x0"))
|
||||
|
||||
(defn bytes-to-hex [x]
|
||||
(when x
|
||||
(subs (.bytesToHex utils x) 2)))
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
(ns status-im.utils.ethereum.core
|
||||
(:require [clojure.string :as string]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.js-dependencies :as dependencies]
|
||||
[status-im.utils.ethereum.tokens :as tokens]
|
||||
[status-im.utils.money :as money]))
|
||||
|
@ -40,6 +39,10 @@
|
|||
address
|
||||
(str hex-prefix address))))
|
||||
|
||||
(defn current-address [db]
|
||||
(-> (get-in db [:account/account :address])
|
||||
normalized-address))
|
||||
|
||||
(defn naked-address [s]
|
||||
(when s
|
||||
(string/replace s hex-prefix "")))
|
||||
|
@ -74,71 +77,6 @@
|
|||
([s opts]
|
||||
(.sha3 dependencies/Web3.prototype (str s) (clj->js opts))))
|
||||
|
||||
(defn hex->string [s]
|
||||
(when s
|
||||
(let [hex (.toString s)]
|
||||
(loop [res "" i (if (string/starts-with? hex hex-prefix) 2 0)]
|
||||
(if (and (< i (.-length hex)))
|
||||
(recur
|
||||
(if (= (.substr hex i 2) "00")
|
||||
res
|
||||
(str res (.fromCharCode js/String (js/parseInt (.substr hex i 2) 16))))
|
||||
(+ i 2))
|
||||
res)))))
|
||||
|
||||
(defn hex->boolean [s]
|
||||
(= s "0x0"))
|
||||
|
||||
(defn boolean->hex [b]
|
||||
(if b "0x0" "0x1"))
|
||||
|
||||
(defn hex->int [s]
|
||||
(if (= s hex-prefix)
|
||||
0
|
||||
(js/parseInt s 16)))
|
||||
|
||||
(defn int->hex [i]
|
||||
(.toHex dependencies/Web3.prototype i))
|
||||
|
||||
(defn hex->bignumber [s]
|
||||
(money/bignumber (if (= s hex-prefix) 0 s)))
|
||||
|
||||
(defn hex->address
|
||||
"When hex value is 66 char in length (2 for 0x, 64 for
|
||||
the 32 bytes used by abi-spec for an address), only keep
|
||||
the part that constitute the address and normalize it,"
|
||||
[s]
|
||||
(when (= 66 (count s))
|
||||
(normalized-address (subs s 26))))
|
||||
|
||||
(defn zero-pad-64 [s]
|
||||
(str (apply str (drop (count s) (repeat 64 "0"))) s))
|
||||
|
||||
(defn string->hex [i]
|
||||
(.fromAscii dependencies/Web3.prototype i))
|
||||
|
||||
(defn format-param [param]
|
||||
(if (number? param)
|
||||
(zero-pad-64 (str (hex->int param)))
|
||||
(zero-pad-64 (subs param 2))))
|
||||
|
||||
(defn format-call-params [method-id & params]
|
||||
(let [params (string/join (map format-param params))]
|
||||
(str method-id params)))
|
||||
|
||||
(defn- sig->method-id [signature]
|
||||
(apply str (take 10 (sha3 signature))))
|
||||
|
||||
(defn call [params callback]
|
||||
(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}))
|
||||
|
||||
(def default-transaction-gas (money/bignumber 21000))
|
||||
|
||||
(defn estimate-gas [symbol]
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
(ns status-im.utils.ethereum.eip165
|
||||
"Utility function related to [EIP165](https://eips.ethereum.org/EIPS/eip-165)"
|
||||
(:require [status-im.utils.ethereum.core :as ethereum]))
|
||||
(:require [status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.utils.ethereum.abi-spec :as abi-spec]))
|
||||
|
||||
(def supports-interface-hash "0x01ffc9a7")
|
||||
(def marker-hash "0xffffffff")
|
||||
|
||||
(defn supports-interface? [contract hash cb]
|
||||
(ethereum/call (ethereum/call-params contract "supportsInterface(bytes4)" hash)
|
||||
#(cb %)))
|
||||
(defn supports-interface?
|
||||
[contract hash cb]
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
:method "supportsInterface(bytes4)"
|
||||
:params [hash]
|
||||
:on-success cb}))
|
||||
|
||||
(defn supports?
|
||||
"Calls cb with true if `supportsInterface` is supported by this contract.
|
||||
|
@ -16,9 +21,9 @@
|
|||
(supports-interface?
|
||||
contract
|
||||
supports-interface-hash
|
||||
#(if (true? (ethereum/hex->boolean %))
|
||||
#(if (true? (abi-spec/hex-to-boolean %))
|
||||
(supports-interface? contract
|
||||
marker-hash
|
||||
(fn [response]
|
||||
(cb (false? (ethereum/hex->boolean response)))))
|
||||
(cb (false? (abi-spec/hex-to-boolean response)))))
|
||||
(cb false))))
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
"
|
||||
(:refer-clojure :exclude [name])
|
||||
(:require [clojure.string :as string]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.ethereum.abi-spec :as abi-spec]))
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.utils.ethereum.core :as ethereum]))
|
||||
|
||||
;; this is the addresses of ens registries for the different networks
|
||||
(def ens-registries
|
||||
|
@ -17,7 +17,7 @@
|
|||
|
||||
(def default-namehash "0000000000000000000000000000000000000000000000000000000000000000")
|
||||
(def default-address "0x0000000000000000000000000000000000000000")
|
||||
(def default-key "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
(def default-key "0x0400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
|
||||
|
||||
(defn namehash
|
||||
[s]
|
||||
|
@ -35,28 +35,37 @@
|
|||
|
||||
(defn resolver
|
||||
[registry ens-name cb]
|
||||
(ethereum/call (ethereum/call-params registry
|
||||
"resolver(bytes32)"
|
||||
(namehash ens-name))
|
||||
(fn [address]
|
||||
(let [address (ethereum/hex->address address)]
|
||||
(cb (if (and address (not= address default-address)) address ""))))))
|
||||
(json-rpc/eth-call
|
||||
{:contract registry
|
||||
:method "resolver(bytes32)"
|
||||
:params [(namehash ens-name)]
|
||||
:outputs ["address"]
|
||||
:on-success
|
||||
(fn [[address]]
|
||||
(when-not (= address default-address)
|
||||
(cb address)))}))
|
||||
|
||||
(defn owner
|
||||
[registry ens-name cb]
|
||||
(ethereum/call (ethereum/call-params registry
|
||||
"owner(bytes32)"
|
||||
(namehash ens-name))
|
||||
(fn [address]
|
||||
(cb address))))
|
||||
(json-rpc/eth-call
|
||||
{:contract registry
|
||||
:method "owner(bytes32)"
|
||||
:params [(namehash ens-name)]
|
||||
:outputs ["address"]
|
||||
:on-success
|
||||
(fn [[address]]
|
||||
(cb address))}))
|
||||
|
||||
(defn ttl
|
||||
[registry ens-name cb]
|
||||
(ethereum/call (ethereum/call-params registry
|
||||
"ttl(bytes32)"
|
||||
(namehash ens-name))
|
||||
(fn [ttl]
|
||||
(cb (ethereum/hex->int ttl)))))
|
||||
(json-rpc/eth-call
|
||||
{:contract registry
|
||||
:method "ttl(bytes32)"
|
||||
:params [(namehash ens-name)]
|
||||
:outputs ["uint256"]
|
||||
:on-success
|
||||
(fn [[ttl]]
|
||||
(cb ttl))}))
|
||||
|
||||
;; Resolver contract
|
||||
;; Resolver must implement EIP65 (supportsInterface). When interacting with an unknown resolver it's safer to rely on it.
|
||||
|
@ -65,9 +74,14 @@
|
|||
|
||||
(defn addr
|
||||
[resolver ens-name cb]
|
||||
(ethereum/call (ethereum/call-params resolver "addr(bytes32)" (namehash ens-name))
|
||||
(fn [address]
|
||||
(cb (ethereum/hex->address address)))))
|
||||
(json-rpc/eth-call
|
||||
{:contract resolver
|
||||
:method "addr(bytes32)"
|
||||
:params [(namehash ens-name)]
|
||||
:outputs ["address"]
|
||||
:on-success
|
||||
(fn [[address]]
|
||||
(cb address))}))
|
||||
|
||||
(def name-hash "0x691f3431")
|
||||
|
||||
|
@ -75,37 +89,41 @@
|
|||
|
||||
(defn name
|
||||
[resolver ens-name cb]
|
||||
(ethereum/call (ethereum/call-params resolver
|
||||
"name(bytes32)"
|
||||
(namehash ens-name))
|
||||
(fn [address]
|
||||
(cb (ethereum/hex->address address)))))
|
||||
(json-rpc/eth-call
|
||||
{:contract resolver
|
||||
:method "name(bytes32)"
|
||||
:params [(namehash ens-name)]
|
||||
:outputs ["string"]
|
||||
:on-success
|
||||
(fn [[name]]
|
||||
(cb name))}))
|
||||
|
||||
(defn contenthash
|
||||
[resolver ens-name cb]
|
||||
(ethereum/call (ethereum/call-params resolver
|
||||
"contenthash(bytes32)"
|
||||
(namehash ens-name))
|
||||
(fn [hash]
|
||||
(cb (first (abi-spec/decode hash ["bytes"]))))))
|
||||
(json-rpc/eth-call
|
||||
{:contract resolver
|
||||
:method "contenthash(bytes32)"
|
||||
:params [(namehash ens-name)]
|
||||
:on-success
|
||||
(fn [hash]
|
||||
(cb hash))}))
|
||||
|
||||
(defn content
|
||||
[resolver ens-name cb]
|
||||
(ethereum/call (ethereum/call-params resolver
|
||||
"content(bytes32)"
|
||||
(namehash ens-name))
|
||||
(fn [hash]
|
||||
(cb hash))))
|
||||
(json-rpc/eth-call
|
||||
{:contract resolver
|
||||
:method "content(bytes32)"
|
||||
:params [(namehash ens-name)]
|
||||
:on-success
|
||||
(fn [hash]
|
||||
(cb hash))}))
|
||||
|
||||
(def ABI-hash "0x2203ab56")
|
||||
(def pubkey-hash "0xc8690233")
|
||||
|
||||
(defn add-uncompressed-public-key-prefix
|
||||
[key]
|
||||
(when (and key
|
||||
(not= "0x" key)
|
||||
(not= default-key key))
|
||||
(str "0x04" (subs key 2))))
|
||||
(defn uncompressed-public-key
|
||||
[x y]
|
||||
(str "0x04" x y))
|
||||
|
||||
(defn is-valid-eth-name?
|
||||
[ens-name]
|
||||
|
@ -115,12 +133,16 @@
|
|||
|
||||
(defn pubkey
|
||||
[resolver ens-name cb]
|
||||
(ethereum/call (ethereum/call-params resolver
|
||||
"pubkey(bytes32)"
|
||||
(namehash ens-name))
|
||||
(fn [key]
|
||||
(when-let [public-key (add-uncompressed-public-key-prefix key)]
|
||||
(cb public-key)))))
|
||||
(json-rpc/eth-call
|
||||
{:contract resolver
|
||||
:method "pubkey(bytes32)"
|
||||
:params [(namehash ens-name)]
|
||||
:outputs ["bytes32" "bytes32"]
|
||||
:on-success
|
||||
(fn [[x y]]
|
||||
(when-let [public-key (uncompressed-public-key x y)]
|
||||
(when-not (= public-key default-key)
|
||||
(cb public-key))))}))
|
||||
|
||||
(defn get-addr
|
||||
[registry ens-name cb]
|
||||
|
|
|
@ -2,19 +2,22 @@
|
|||
"
|
||||
Helper functions to interact with [ERC721](https://eips.ethereum.org/EIPS/eip-721) smart contract
|
||||
"
|
||||
(:require [status-im.utils.ethereum.core :as ethereum]))
|
||||
(:require [status-im.ethereum.json-rpc :as json-rpc]))
|
||||
|
||||
(defn token-of-owner-by-index [contract address index cb]
|
||||
(ethereum/call (ethereum/call-params
|
||||
contract
|
||||
"tokenOfOwnerByIndex(address,uint256)"
|
||||
(ethereum/normalized-address address)
|
||||
(ethereum/int->hex index))
|
||||
#(cb (ethereum/hex->bignumber %))))
|
||||
(defn token-of-owner-by-index
|
||||
[contract address index cb]
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
:method "tokenOfOwnerByIndex(address,uint256)"
|
||||
:params [address index]
|
||||
:outputs ["uint256"]
|
||||
:on-success (fn [[token]] (cb token))}))
|
||||
|
||||
(defn token-uri [contract tokenId cb]
|
||||
(ethereum/call (ethereum/call-params
|
||||
contract
|
||||
"tokenURI(uint256)"
|
||||
(ethereum/int->hex tokenId))
|
||||
#(cb (ethereum/hex->string %))))
|
||||
(defn token-uri
|
||||
[contract tokenId cb]
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
:method "tokenURI(uint256)"
|
||||
:params [tokenId]
|
||||
:outputs ["string"]
|
||||
:on-success (fn [[uri]] (cb uri))}))
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
(:refer-clojure :exclude [name]))
|
||||
|
||||
(def default-hash "0x0000000000000000000000000000000000000000000000000000000000000000")
|
||||
|
||||
(defn contenthash [registry ens-name cb]
|
||||
(ens/resolver registry
|
||||
ens-name
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
(ns status-im.utils.ethereum.stickers
|
||||
(:require [status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.ethereum.abi-spec :as abi-spec]))
|
||||
|
||||
(def contracts
|
||||
{:mainnet nil
|
||||
:testnet "0x39d16CdB56b5a6a89e1A397A13Fe48034694316E"
|
||||
:rinkeby nil})
|
||||
|
||||
(defn pack-count
|
||||
"Returns number of packs rigestered in the contract"
|
||||
[contract cb]
|
||||
(ethereum/call (ethereum/call-params contract "packCount()")
|
||||
(fn [count] (cb (ethereum/hex->int count)))))
|
||||
|
||||
(defn pack-data
|
||||
"Returns vector of pack data parameters by pack id: [category owner mintable timestamp price contenthash]"
|
||||
[contract pack-id cb]
|
||||
(ethereum/call (ethereum/call-params contract "getPackData(uint256)" (ethereum/int->hex pack-id))
|
||||
(fn [data]
|
||||
(cb (abi-spec/decode data ["bytes4[]" "address" "bool" "uint256" "uint256" "bytes"])))))
|
||||
|
||||
(defn owned-tokens
|
||||
"Returns vector of owned tokens ids in the contract by address"
|
||||
[contract address cb]
|
||||
(ethereum/call (ethereum/call-params contract "tokensOwnedBy(address)" (ethereum/normalized-address address))
|
||||
(fn [data]
|
||||
(cb (first (abi-spec/decode data ["uint256[]"]))))))
|
||||
|
||||
(defn token-pack-id
|
||||
"Returns pack id in the contract by token id"
|
||||
[contract token cb]
|
||||
(ethereum/call (ethereum/call-params contract "tokenPackId(uint256)" (ethereum/int->hex token))
|
||||
(fn [data] (cb (ethereum/hex->int data)))))
|
|
@ -9,6 +9,7 @@
|
|||
[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.abi-spec :as abi-spec]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[status-im.utils.ethereum.tokens :as tokens]
|
||||
[status-im.utils.fx :as fx]
|
||||
|
@ -256,9 +257,9 @@
|
|||
[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)
|
||||
:gas (ethereum/int->hex gas)
|
||||
:gasPrice (ethereum/int->hex gas-price)}
|
||||
:value (str "0x" (abi-spec/number-to-hex amount))
|
||||
:gas (str "0x" (abi-spec/number-to-hex gas))
|
||||
:gasPrice (str "0x" (abi-spec/number-to-hex gas-price))}
|
||||
data
|
||||
(assoc :data data)
|
||||
nonce
|
||||
|
@ -314,15 +315,15 @@
|
|||
(when on-error
|
||||
{:dispatch (conj on-error "transaction was cancelled by user")}))))
|
||||
|
||||
(defn prepare-unconfirmed-transaction [db now hash]
|
||||
(defn prepare-unconfirmed-transaction
|
||||
[db now hash]
|
||||
(let [transaction (get-in db [:wallet :send-transaction])
|
||||
all-tokens (:wallet/all-tokens db)]
|
||||
(let [chain (:chain db)
|
||||
token (tokens/symbol->token all-tokens (keyword chain) (:symbol transaction))]
|
||||
(-> transaction
|
||||
(assoc :confirmations "0"
|
||||
:timestamp (str now)
|
||||
:type :outbound
|
||||
(assoc :timestamp (str now)
|
||||
:type :pending
|
||||
:hash hash
|
||||
:value (:amount transaction)
|
||||
:token token
|
||||
|
@ -466,30 +467,6 @@
|
|||
:wallet-send-modal-stack
|
||||
:wallet-send-modal-stack-with-onboarding)]]}))
|
||||
|
||||
(fx/defn open-sign-transaction-flow
|
||||
[{: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)]
|
||||
(fx/merge cofx
|
||||
(cond-> {: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))}
|
||||
(not gas)
|
||||
(assoc :wallet/update-estimated-gas
|
||||
{:obj (select-keys transaction [:to :data])
|
||||
:success-event :wallet/update-estimated-gas-success})
|
||||
|
||||
(not gas-price)
|
||||
(assoc :wallet/update-gas-price
|
||||
{:success-event :wallet/update-gas-price-success
|
||||
:edit? false}))
|
||||
(navigation/navigate-to-cofx go-to-view-id {}))))
|
||||
|
||||
(defn send-transaction-screen-did-load
|
||||
[{:keys [db]}]
|
||||
{:db (assoc-in db
|
||||
|
@ -575,3 +552,34 @@
|
|||
(toggle-visible-token symbol true)
|
||||
;;TODO(goranjovic): move `update-token-balance-success` function to wallet models
|
||||
(update-token-balance symbol balance)))
|
||||
|
||||
(fx/defn eth-transaction-call
|
||||
[{:keys [db] :as cofx}
|
||||
{:keys [contract method params on-success on-error details] :as transaction}]
|
||||
(let [current-address (ethereum/current-address db)
|
||||
transaction (merge {:to contract
|
||||
:from current-address
|
||||
:data (abi-spec/encode method params)
|
||||
:id "approve"
|
||||
:symbol :ETH
|
||||
:method "eth_sendTransaction"
|
||||
:amount (money/bignumber 0)
|
||||
:on-success on-success
|
||||
:on-error on-error}
|
||||
details)
|
||||
go-to-view-id (if (get-in db [:account/account :wallet-set-up-passed?])
|
||||
:wallet-send-modal-stack
|
||||
:wallet-send-modal-stack-with-onboarding)]
|
||||
(fx/merge cofx
|
||||
{:db (-> db
|
||||
(assoc-in [:navigation/screen-params :wallet-send-modal-stack :modal?] true)
|
||||
(assoc-in [:wallet :send-transaction]
|
||||
transaction))
|
||||
:wallet/update-estimated-gas
|
||||
{:obj (select-keys transaction [:to :from :data])
|
||||
:success-event :wallet/update-estimated-gas-success}
|
||||
|
||||
:wallet/update-gas-price
|
||||
{:success-event :wallet/update-gas-price-success
|
||||
:edit? false}}
|
||||
(navigation/navigate-to-cofx go-to-view-id {}))))
|
||||
|
|
|
@ -1,136 +1,210 @@
|
|||
(ns status-im.wallet.custom-tokens.core
|
||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
||||
(:require [re-frame.core :as re-frame]
|
||||
(:require [clojure.string :as string]
|
||||
[re-frame.core :as re-frame]
|
||||
[status-im.ethereum.decode :as decode]
|
||||
[status-im.ethereum.json-rpc :as json-rpc]
|
||||
[status-im.i18n :as i18n]
|
||||
[status-im.ui.components.colors :as colors]
|
||||
[status-im.ui.components.react :as react]
|
||||
[status-im.utils.ethereum.core :as ethereum]
|
||||
[clojure.string :as string]
|
||||
[status-im.ethereum.decode :as decode]
|
||||
[status-im.utils.money :as money]
|
||||
[status-im.utils.fx :as fx]
|
||||
[status-im.wallet.core :as wallet]))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wallet.custom-token/get-decimals
|
||||
(fn [contract]
|
||||
(ethereum/call
|
||||
(ethereum/call-params contract "decimals()")
|
||||
#(re-frame/dispatch [:wallet.custom-token/decimals-result %]))))
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
:method "decimals()"
|
||||
:outputs ["uint256"]
|
||||
:on-success
|
||||
(fn [[contract-decimals]]
|
||||
(re-frame/dispatch [:wallet.custom-token/decimals-result
|
||||
contract-decimals]))})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wallet.custom-token/get-symbol
|
||||
(fn [contract]
|
||||
(ethereum/call
|
||||
(ethereum/call-params contract "symbol()")
|
||||
#(re-frame/dispatch [:wallet.custom-token/symbol-result contract %]))))
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
:method "symbol()"
|
||||
:outputs ["string"]
|
||||
:on-success
|
||||
(fn [[contract-symbol]]
|
||||
(re-frame/dispatch [:wallet.custom-token/symbol-result
|
||||
contract
|
||||
contract-symbol]))})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wallet.custom-token/get-balance
|
||||
(fn [[contract address]]
|
||||
(ethereum/call
|
||||
(ethereum/call-params contract "balanceOf(address)" (ethereum/normalized-address address))
|
||||
#(re-frame/dispatch [:wallet.custom-token/balance-result contract %]))))
|
||||
(fn [[contract wallet-address]]
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
:method "balanceOf(address)"
|
||||
:params [wallet-address]
|
||||
:outputs ["uint256"]
|
||||
:on-success
|
||||
(fn [[balance]]
|
||||
(re-frame/dispatch [:wallet.custom-token/balance-result
|
||||
contract
|
||||
(money/bignumber balance)]))})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wallet.custom-token/get-name
|
||||
(fn [contract]
|
||||
(ethereum/call
|
||||
(ethereum/call-params contract "name()")
|
||||
#(re-frame/dispatch [:wallet.custom-token/name-result contract %]))))
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
:method "name()"
|
||||
:outputs ["string"]
|
||||
:on-success
|
||||
(fn [[contract-name]]
|
||||
(re-frame/dispatch [:wallet.custom-token/name-result
|
||||
contract
|
||||
contract-name]))})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wallet.custom-token/get-total-supply
|
||||
(fn [contract]
|
||||
(ethereum/call
|
||||
(ethereum/call-params contract "totalSupply()")
|
||||
#(re-frame/dispatch [:wallet.custom-token/total-supply-result contract %]))))
|
||||
(json-rpc/eth-call
|
||||
{:contract contract
|
||||
:method "totalSupply()"
|
||||
:outputs ["uint256"]
|
||||
:on-success
|
||||
(fn [[contract-total-supply]]
|
||||
(re-frame/dispatch [:wallet.custom-token/total-supply-result
|
||||
contract
|
||||
(money/bignumber contract-total-supply)]))})))
|
||||
|
||||
(re-frame/reg-fx
|
||||
:wallet.custom-token/contract-address-paste
|
||||
(fn []
|
||||
(react/get-from-clipboard #(re-frame/dispatch [:wallet.custom-token/contract-address-is-pasted (string/trim %)]))))
|
||||
(react/get-from-clipboard
|
||||
#(re-frame/dispatch [:wallet.custom-token/contract-address-is-pasted
|
||||
(string/trim %)]))))
|
||||
|
||||
(defn field-exists? [{:wallet/keys [all-tokens] :as db} field-key field-value]
|
||||
(defn field-exists?
|
||||
[{:wallet/keys [all-tokens] :as db} field-key field-value]
|
||||
(let [chain-key (ethereum/get-chain-keyword db)]
|
||||
(some #(= field-value (get % field-key)) (vals (get all-tokens chain-key)))))
|
||||
(some #(= field-value (get % field-key))
|
||||
(vals (get all-tokens chain-key)))))
|
||||
|
||||
(fx/defn total-supply-result [{:keys [db]} contract result]
|
||||
(if (and (string? result) (string/starts-with? result "0x") (> (count result) 2))
|
||||
{:wallet.custom-token/get-balance [contract (get-in db [:account/account :address])]}
|
||||
{:db (update db :wallet/custom-token-screen merge {:in-progress? nil :error (i18n/label :t/wrong-contract)})}))
|
||||
(fx/defn total-supply-result
|
||||
[{:keys [db]} contract total-supply]
|
||||
(if (money/valid? total-supply)
|
||||
{:wallet.custom-token/get-balance
|
||||
[contract (ethereum/current-address db)]}
|
||||
{:db (update db
|
||||
:wallet/custom-token-screen
|
||||
merge {:in-progress? nil
|
||||
:error (i18n/label :t/wrong-contract)})}))
|
||||
|
||||
(defn token-in-list? [{:wallet/keys [all-tokens] :as db} contract]
|
||||
(defn token-in-list?
|
||||
[{:wallet/keys [all-tokens] :as db} contract]
|
||||
(let [chain-key (ethereum/get-chain-keyword db)
|
||||
addresses (set (map string/lower-case (keys (get all-tokens chain-key))))]
|
||||
(not (nil? (get addresses (string/lower-case contract))))))
|
||||
|
||||
(fx/defn contract-address-is-changed [{:keys [db]} contract]
|
||||
(fx/defn contract-address-is-changed
|
||||
[{:keys [db]} contract]
|
||||
(if (ethereum/address? contract)
|
||||
(if (token-in-list? db contract)
|
||||
{:db (assoc db :wallet/custom-token-screen {:contract contract :error (i18n/label :t/already-have-asset)})}
|
||||
{:db (assoc db :wallet/custom-token-screen {:contract contract :in-progress? true})
|
||||
{:db (assoc db
|
||||
:wallet/custom-token-screen
|
||||
{:contract contract :error (i18n/label :t/already-have-asset)})}
|
||||
{:db (assoc db
|
||||
:wallet/custom-token-screen
|
||||
{:contract contract :in-progress? true})
|
||||
:wallet.custom-token/get-total-supply contract})
|
||||
{:db (assoc db :wallet/custom-token-screen {:contract contract :error (i18n/label :t/wrong-address)})}))
|
||||
{:db (assoc db
|
||||
:wallet/custom-token-screen
|
||||
{:contract contract
|
||||
:error (i18n/label :t/wrong-address)})}))
|
||||
|
||||
(fx/defn decimals-result [{:keys [db]} result]
|
||||
{:db (update db :wallet/custom-token-screen merge {:decimals (str (decode/uint result))
|
||||
:in-progress? nil})})
|
||||
(fx/defn decimals-result
|
||||
[{:keys [db]} result]
|
||||
{:db (update db
|
||||
:wallet/custom-token-screen
|
||||
merge
|
||||
{:decimals (str (decode/uint result))
|
||||
:in-progress? nil})})
|
||||
|
||||
(fx/defn symbol-result [{:keys [db]} contract result]
|
||||
(let [token-symbol (decode/string result)
|
||||
symbol-exists? (field-exists? db :symbol (keyword token-symbol))]
|
||||
(fx/defn symbol-result
|
||||
[{:keys [db]} contract token-symbol]
|
||||
(let [symbol-exists? (field-exists? db :symbol (keyword token-symbol))]
|
||||
{:db
|
||||
(update db :wallet/custom-token-screen merge
|
||||
(update db
|
||||
:wallet/custom-token-screen merge
|
||||
{:symbol token-symbol
|
||||
:error-symbol (when symbol-exists?
|
||||
(i18n/label :t/you-already-have-an-asset {:value token-symbol}))})
|
||||
:wallet.custom-token/get-decimals
|
||||
contract}))
|
||||
|
||||
(fx/defn name-result [{:keys [db]} contract result]
|
||||
(let [token-name (decode/string result)
|
||||
name-exists? (field-exists? db :name token-name)]
|
||||
(fx/defn name-result
|
||||
[{:keys [db]} contract token-name]
|
||||
(let [name-exists? (field-exists? db :name token-name)]
|
||||
{:db
|
||||
(update db :wallet/custom-token-screen merge
|
||||
{:name token-name
|
||||
:error-name (when name-exists?
|
||||
(i18n/label :t/you-already-have-an-asset {:value token-name}))})
|
||||
:wallet.custom-token/get-symbol
|
||||
contract}))
|
||||
(i18n/label :t/you-already-have-an-asset
|
||||
{:value token-name}))})
|
||||
:wallet.custom-token/get-symbol contract}))
|
||||
|
||||
(fx/defn balance-result [{:keys [db]} contract result]
|
||||
(if (and (string? result) (string/starts-with? result "0x") (> (count result) 2))
|
||||
{:db (assoc-in db [:wallet/custom-token-screen :balance] (str (decode/uint result)))
|
||||
(fx/defn balance-result
|
||||
[{:keys [db]} contract balance]
|
||||
(if (money/valid? balance)
|
||||
{:db (assoc-in db
|
||||
[:wallet/custom-token-screen :balance]
|
||||
(str balance))
|
||||
:wallet.custom-token/get-name contract}
|
||||
{:db (update db :wallet/custom-token-screen merge {:in-progress? nil :error (i18n/label :t/wrong-contract)})}))
|
||||
{:db (update db
|
||||
:wallet/custom-token-screen
|
||||
merge
|
||||
{:in-progress? nil
|
||||
:error (i18n/label :t/wrong-contract)})}))
|
||||
|
||||
(fx/defn add-custom-token [{:keys [db] :as cofx}]
|
||||
(fx/defn add-custom-token
|
||||
[{:keys [db] :as cofx}]
|
||||
(let [{:keys [contract name symbol decimals]} (get db :wallet/custom-token-screen)
|
||||
chain-key (ethereum/get-chain-keyword db)
|
||||
symbol (keyword symbol)
|
||||
new-token {:address contract :name name :symbol symbol :custom? true
|
||||
:decimals (int decimals) :color (rand-nth colors/chat-colors)}]
|
||||
(fx/merge (assoc-in cofx [:db :wallet/all-tokens chain-key contract] new-token)
|
||||
new-token {:address contract
|
||||
:name name
|
||||
:symbol symbol
|
||||
:custom? true
|
||||
:decimals (int decimals)
|
||||
:color (rand-nth colors/chat-colors)}]
|
||||
(fx/merge (assoc-in cofx
|
||||
[:db :wallet/all-tokens chain-key contract]
|
||||
new-token)
|
||||
(wallet/add-custom-token new-token))))
|
||||
|
||||
(fx/defn remove-custom-token [{:keys [db] :as cofx} {:keys [address] :as token}]
|
||||
(fx/defn remove-custom-token
|
||||
[{:keys [db] :as cofx} {:keys [address] :as token}]
|
||||
(let [chain-key (ethereum/get-chain-keyword db)]
|
||||
(fx/merge (update-in cofx [:db :wallet/all-tokens chain-key] dissoc address)
|
||||
(wallet/remove-custom-token token))))
|
||||
|
||||
(fx/defn field-is-edited [{:keys [db] :as cofx} field-key value]
|
||||
(fx/defn field-is-edited
|
||||
[{:keys [db] :as cofx} field-key value]
|
||||
(case field-key
|
||||
:contract (contract-address-is-changed cofx value)
|
||||
:name {:db (update db :wallet/custom-token-screen merge
|
||||
:name {:db (update db
|
||||
:wallet/custom-token-screen merge
|
||||
{field-key
|
||||
value
|
||||
:error-name
|
||||
(when (field-exists? db field-key value)
|
||||
(i18n/label :t/you-already-have-an-asset {:value value}))})}
|
||||
(i18n/label :t/you-already-have-an-asset
|
||||
{:value value}))})}
|
||||
:symbol {:db (update db :wallet/custom-token-screen merge
|
||||
{field-key
|
||||
value
|
||||
:error-symbol
|
||||
(when (field-exists? db field-key (keyword value))
|
||||
(i18n/label :t/you-already-have-an-asset {:value value}))})}
|
||||
:decimals {:db (assoc-in db [:wallet/custom-token-screen :decimals] value)}))
|
||||
:decimals {:db (assoc-in db
|
||||
[:wallet/custom-token-screen :decimals]
|
||||
value)}))
|
||||
|
|
|
@ -1,17 +1,9 @@
|
|||
(ns status-im.test.utils.ethereum.core
|
||||
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||
(:require [cljs.test :refer-macros [deftest is]]
|
||||
[status-im.utils.ethereum.core :as ethereum]))
|
||||
|
||||
(deftest call-params
|
||||
(testing "ERC20 balance-of params"
|
||||
(let [contract "0x29b5f6efad2ad701952dfde9f29c960b5d6199c5"
|
||||
address "0xa7cfd581060ec66414790691681732db249502bd"]
|
||||
(is (= (ethereum/call-params contract "balanceOf(address)" address)
|
||||
{:to "0x29b5f6efad2ad701952dfde9f29c960b5d6199c5"
|
||||
:data "0x70a08231000000000000000000000000a7cfd581060ec66414790691681732db249502bd"})))))
|
||||
|
||||
(deftest chain-id->chain-keyword
|
||||
(is (= (ethereum/chain-id->chain-keyword 1) :mainnet))
|
||||
(is (= (ethereum/chain-id->chain-keyword 3) :testnet))
|
||||
(is (= (ethereum/chain-id->chain-keyword 4) :rinkeby))
|
||||
(is (= (ethereum/chain-id->chain-keyword 5777) :custom)))
|
||||
(is (= (ethereum/chain-id->chain-keyword 5777) :custom)))
|
||||
|
|
Loading…
Reference in New Issue