[BUG #2029] Properly handle gas details from payment requests

This commit is contained in:
Julien Eluard 2018-01-05 08:12:56 +01:00 committed by Oskar Thoren
parent 0a1c3bcd88
commit f90e1a4ed3
No known key found for this signature in database
GPG Key ID: 5128AB0637CD85AF
12 changed files with 134 additions and 66 deletions

View File

@ -3,7 +3,8 @@
[status-im.i18n :as i18n] [status-im.i18n :as i18n]
[status-im.utils.ethereum.core :as ethereum] [status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.eip681 :as eip681] [status-im.utils.ethereum.eip681 :as eip681]
[status-im.utils.handlers :as handlers])) [status-im.utils.handlers :as handlers]
[status-im.utils.money :as money]))
(handlers/register-handler-db (handlers/register-handler-db
:wallet/toggle-flashlight :wallet/toggle-flashlight
@ -12,37 +13,44 @@
toggled-state (if (= :on flashlight-state) :off :on)] toggled-state (if (= :on flashlight-state) :off :on)]
(assoc-in db [:wallet :send-transaction :camera-flashlight] toggled-state)))) (assoc-in db [:wallet :send-transaction :camera-flashlight] toggled-state))))
(defn- fill-request-details [db address name amount] (defn- fill-request-details [db {:keys [address name value symbol gas gasPrice] :as m}]
{:pre [(not (nil? address))]}
(update-in (update-in
db [:wallet :send-transaction] db [:wallet :send-transaction]
#(cond-> (assoc % :to address :to-name name) #(cond-> (assoc % :to address)
amount (assoc :amount amount)))) value (assoc :amount value)
name (assoc :to-name name)
symbol (assoc :symbol symbol)
gas (assoc :gas (money/bignumber gas))
gasPrice (assoc :gas-price (money/bignumber gasPrice))
(and symbol (not gasPrice))
(assoc :gas-price (ethereum/estimate-gas symbol)))))
(defn- extract-details (defn- extract-details
"First try to parse as EIP681 URI, if not assume this is an address directly. "First try to parse as EIP681 URI, if not assume this is an address directly.
Returns a map containing at least the `address`, `symbol` and `chain-id` keys" Returns a map containing at least the `address` and `chain-id` keys"
[s chain-id] [s chain-id]
(or (let [m (eip681/parse-uri s)] (or (let [m (eip681/parse-uri s)]
(merge m (eip681/extract-request-details m))) (merge m (eip681/extract-request-details m)))
(when (ethereum/address? s) (when (ethereum/address? s)
{:address s :chain-id chain-id :symbol :ETH}))) {:address s :chain-id chain-id})))
(handlers/register-handler-fx (handlers/register-handler-fx
:wallet/fill-request-from-url :wallet/fill-request-from-url
(fn [{{:keys [web3 network] :as db} :db} [_ data name]] (fn [{{:keys [web3 network] :as db} :db} [_ data name]]
(let [{:keys [view-id]} db (let [{:keys [view-id]} db
current-chain-id (get-in constants/default-networks [network :raw-config :NetworkId]) current-chain-id (get-in constants/default-networks [network :raw-config :NetworkId])
{:keys [address chain-id value]} (extract-details data current-chain-id) {:keys [address chain-id] :as details} (extract-details data current-chain-id)
valid-network? (boolean (= current-chain-id chain-id))] valid-network? (boolean (= current-chain-id chain-id))]
(cond-> {:db db} (cond-> {:db db}
(and address (= :choose-recipient view-id)) (assoc :dispatch [:navigate-back]) (and address (= :choose-recipient view-id)) (assoc :dispatch [:navigate-back])
(and address valid-network?) (update :db #(fill-request-details % address name value)) (and address valid-network?) (update :db #(fill-request-details % details))
(not address) (assoc :show-error (i18n/label :t/wallet-invalid-address {:data data})) (not address) (assoc :show-error (i18n/label :t/wallet-invalid-address {:data data}))
(and address (not valid-network?)) (assoc :show-error (i18n/label :t/wallet-invalid-chain-id {:data data :chain current-chain-id})))))) (and address (not valid-network?)) (assoc :show-error (i18n/label :t/wallet-invalid-chain-id {:data data :chain current-chain-id}))))))
(handlers/register-handler-fx (handlers/register-handler-fx
:wallet/fill-request-from-contact :wallet/fill-request-from-contact
(fn [{db :db} [_ {:keys [address name]}]] (fn [{db :db} [_ {:keys [address name]}]]
{:db (fill-request-details db address name nil) {:db (fill-request-details db {:address address :name name})
:dispatch-n [[:navigate-back] :dispatch-n [[:navigate-back]
[:navigate-back]]})) [:navigate-back]]}))

View File

@ -62,17 +62,16 @@
[react/text [react/text
(name symbol)]]])) (name symbol)]]]))
(views/defview choose-currency [style] (views/defview choose-currency [{:keys [style on-change value]}]
(views/letsubs [visible-tokens [:wallet.settings/visible-tokens] (views/letsubs [visible-tokens [:wallet.settings/visible-tokens]]
symbol [:wallet.send/symbol]]
[react/view [react/view
[react/text {:style styles/label} (i18n/label :t/currency)] [react/text {:style styles/label} (i18n/label :t/currency)]
[react/view (merge styles/currency-container [react/view (merge styles/currency-container
style) style)
[react/picker {:selected (name symbol) [react/picker {:selected value
:style {:color "white"} :style {:color "white"}
:item-style styles/wallet-name :item-style styles/wallet-name
:on-change #(re-frame/dispatch [:wallet.send/set-symbol (keyword %)])} :on-change on-change}
(map (fn [s] {:value (name s) :color "white"}) (conj visible-tokens (:symbol tokens/ethereum)))]]])) (map (fn [s] {:value (name s) :color "white"}) (conj visible-tokens (:symbol tokens/ethereum)))]]]))
(defn choose-recipient-content [{:keys [address name on-press style]}] (defn choose-recipient-content [{:keys [address name on-press style]}]

View File

@ -5,6 +5,7 @@
(spec/def ::amount (spec/nilable money/valid?)) (spec/def ::amount (spec/nilable money/valid?))
(spec/def ::amount-error (spec/nilable string?)) (spec/def ::amount-error (spec/nilable string?))
(spec/def ::symbol (spec/nilable keyword?))
(spec/def :wallet/request-transaction (allowed-keys (spec/def :wallet/request-transaction (allowed-keys
:opt-un [::amount ::amount-error])) :opt-un [::amount ::amount-error ::symbol]))

View File

@ -32,3 +32,8 @@
{:db (-> db {:db (-> db
(assoc-in [:wallet/request-transaction :amount] (money/ether->wei value)) (assoc-in [:wallet/request-transaction :amount] (money/ether->wei value))
(assoc-in [:wallet/request-transaction :amount-error] error))}))) (assoc-in [:wallet/request-transaction :amount-error] error))})))
(handlers/register-handler-fx
:wallet.request/set-symbol
(fn [{:keys [db]} [_ s]]
{:db (assoc-in db [:wallet/request-transaction :symbol] s)}))

View File

@ -1,11 +1,14 @@
(ns status-im.ui.screens.wallet.request.subs (ns status-im.ui.screens.wallet.request.subs
(:require [re-frame.core :as re-frame])) (:require [re-frame.core :as re-frame]
[status-im.utils.ethereum.tokens :as tokens]))
(re-frame/reg-sub (re-frame/reg-sub
:wallet.request/request-enabled? :wallet.request/request-enabled?
:<- [:get-in [:wallet/request-transaction :amount]] :<- [:get-in [:wallet/request-transaction :amount]]
:<- [:get-in [:wallet/request-transaction :amount-error]] :<- [:get-in [:wallet/request-transaction :amount-error]]
(fn [[amount amount-error]] :<- [:get-in [:wallet/request-transaction :symbol]]
(fn [[amount amount-error symbol]]
(and (and
(or (nil? symbol) (tokens/ethereum? symbol))
(nil? amount-error) (nil? amount-error)
(not (nil? amount))))) (not (nil? amount)))))

View File

@ -1,7 +1,6 @@
(ns status-im.ui.screens.wallet.request.views (ns status-im.ui.screens.wallet.request.views
(:require-macros [status-im.utils.views :as views]) (:require-macros [status-im.utils.views :as views])
(:require (:require [re-frame.core :as re-frame]
[re-frame.core :as re-frame]
[status-im.ui.components.react :as react] [status-im.ui.components.react :as react]
[status-im.ui.components.qr-code :as components.qr-code] [status-im.ui.components.qr-code :as components.qr-code]
[status-im.ui.components.toolbar.actions :as actions] [status-im.ui.components.toolbar.actions :as actions]
@ -17,6 +16,7 @@
[status-im.utils.platform :as platform] [status-im.utils.platform :as platform]
[status-im.utils.ethereum.core :as ethereum] [status-im.utils.ethereum.core :as ethereum]
[status-im.utils.ethereum.eip681 :as eip681] [status-im.utils.ethereum.eip681 :as eip681]
[status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.money :as money])) [status-im.utils.money :as money]))
(defn toolbar-view [] (defn toolbar-view []
@ -31,17 +31,25 @@
:action :request :action :request
:params {:hide-actions? true}}])) :params {:hide-actions? true}}]))
(views/defview qr-code [amount] (defn- generate-value [address {:keys [symbol chain-id] :as m}]
(if (tokens/ethereum? symbol)
(eip681/generate-uri address (dissoc m :symbol))
(eip681/generate-erc20-uri address m)))
(views/defview qr-code [amount symbol]
(views/letsubs [account [:get-current-account] (views/letsubs [account [:get-current-account]
chain-id [:get-network-id]] chain-id [:get-network-id]]
[components.qr-code/qr-code [components.qr-code/qr-code
{:value (eip681/generate-uri (ethereum/normalized-address (:address account)) (merge {:chain-id chain-id} (when amount {:value amount}))) (let [address (ethereum/normalized-address (:address account))
:size 256}])) params {:chain-id chain-id :value amount :symbol (or symbol :ETH)}]
{:value (generate-value address params)
:size 256})]))
(views/defview request-transaction [] (views/defview request-transaction []
;;Because input field is in the end of view we will scroll to the end on input focus event ;;Because input field is in the end of view we will scroll to the end on input focus event
(views/letsubs [amount [:get-in [:wallet/request-transaction :amount]] (views/letsubs [amount [:get-in [:wallet/request-transaction :amount]]
amount-error [:get-in [:wallet/request-transaction :amount-error]] amount-error [:get-in [:wallet/request-transaction :amount-error]]
symbol [:get-in [:wallet/request-transaction :symbol]]
request-enabled? [:wallet.request/request-enabled?] request-enabled? [:wallet.request/request-enabled?]
scroll (atom nil)] scroll (atom nil)]
[react/keyboard-avoiding-view wallet.styles/wallet-modal-container [react/keyboard-avoiding-view wallet.styles/wallet-modal-container
@ -52,7 +60,7 @@
[react/view components.styles/flex [react/view components.styles/flex
[react/view styles/network-container [react/view styles/network-container
[react/view styles/qr-container [react/view styles/qr-container
[qr-code amount]]] [qr-code amount symbol]]]
[react/view wallet.styles/choose-wallet-container [react/view wallet.styles/choose-wallet-container
[components/choose-wallet]] [components/choose-wallet]]
[react/view wallet.styles/amount-container [react/view wallet.styles/amount-container
@ -61,7 +69,9 @@
:input-options {:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100))) :input-options {:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100)))
:on-change-text #(re-frame/dispatch [:wallet.request/set-and-validate-amount %])}}] :on-change-text #(re-frame/dispatch [:wallet.request/set-and-validate-amount %])}}]
[react/view wallet.styles/choose-currency-container [react/view wallet.styles/choose-currency-container
[components/choose-currency wallet.styles/choose-currency]]]]] [components/choose-currency {:style wallet.styles/choose-currency
:on-change #(re-frame/dispatch [:wallet.request/set-symbol (keyword %)])
:value (name (or symbol :ETH))}]]]]]
[components/separator] [components/separator]
[react/view wallet.styles/buttons-container [react/view wallet.styles/buttons-container
[react/touchable-highlight {:style wallet.styles/button :disabled true} [react/touchable-highlight {:style wallet.styles/button :disabled true}

View File

@ -70,17 +70,11 @@
(assoc-in [:wallet :send-transaction :amount] (money/ether->wei value)) (assoc-in [:wallet :send-transaction :amount] (money/ether->wei value))
(assoc-in [:wallet :send-transaction :amount-error] error))}))) (assoc-in [:wallet :send-transaction :amount-error] error))})))
(defn- estimated-gas [symbol]
(if (tokens/ethereum? symbol)
ethereum/default-transaction-gas
;; TODO(jeluard) Rely on estimateGas call
(.times ethereum/default-transaction-gas 5)))
(handlers/register-handler-fx (handlers/register-handler-fx
:wallet.send/set-symbol :wallet.send/set-symbol
(fn [{:keys [db]} [_ symbol]] (fn [{:keys [db]} [_ symbol]]
{:db (-> (assoc-in db [:wallet :send-transaction :symbol] symbol) {:db (-> (assoc-in db [:wallet :send-transaction :symbol] symbol)
(assoc-in [:wallet :send-transaction :gas] (estimated-gas symbol)))})) (assoc-in [:wallet :send-transaction :gas] (ethereum/estimate-gas symbol)))}))
(handlers/register-handler-fx (handlers/register-handler-fx
:wallet.send/toggle-advanced :wallet.send/toggle-advanced

View File

@ -189,7 +189,7 @@
(when on-press (when on-press
[vector-icons/icon :icons/forward {:color :white}])]]]]) [vector-icons/icon :icons/forward {:color :white}])]]]])
(defn- send-transaction-panel [{:keys [modal? transaction scroll advanced?] :as transaction}] (defn- send-transaction-panel [{:keys [modal? transaction scroll advanced? symbol]}]
(let [{:keys [amount amount-error signing? to to-name sufficient-funds? in-progress? from-chat?]} transaction] (let [{:keys [amount amount-error signing? to to-name sufficient-funds? in-progress? from-chat?]} transaction]
[react/keyboard-avoiding-view wallet.styles/wallet-modal-container [react/keyboard-avoiding-view wallet.styles/wallet-modal-container
[react/view components.styles/flex [react/view components.styles/flex
@ -219,7 +219,9 @@
[react/view wallet.styles/choose-currency-container [react/view wallet.styles/choose-currency-container
[components/view-currency wallet.styles/choose-currency]] [components/view-currency wallet.styles/choose-currency]]
[react/view wallet.styles/choose-currency-container [react/view wallet.styles/choose-currency-container
[components/choose-currency wallet.styles/choose-currency]])] [components/choose-currency {:style wallet.styles/choose-currency
:on-change #(re-frame/dispatch [:wallet.send/set-symbol (keyword %)])
:value (name symbol)}]])]
[react/view {:style send.styles/advanced-wrapper} [react/view {:style send.styles/advanced-wrapper}
[react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet.send/toggle-advanced (not advanced?)])} [react/touchable-highlight {:on-press #(re-frame/dispatch [:wallet.send/toggle-advanced (not advanced?)])}
[react/view {:style send.styles/advanced-button-wrapper} [react/view {:style send.styles/advanced-button-wrapper}
@ -245,15 +247,17 @@
(defview send-transaction [] (defview send-transaction []
(letsubs [transaction [:wallet.send/transaction] (letsubs [transaction [:wallet.send/transaction]
symbol [:wallet.send/symbol]
advanced? [:wallet.send/advanced?] advanced? [:wallet.send/advanced?]
scroll (atom nil)] scroll (atom nil)]
[send-transaction-panel {:modal? false :transaction transaction :scroll scroll :advanced? advanced?}])) [send-transaction-panel {:modal? false :transaction transaction :scroll scroll :advanced? advanced? :symbol symbol}]))
(defview send-transaction-modal [] (defview send-transaction-modal []
(letsubs [transaction [:wallet.send/unsigned-transaction] (letsubs [transaction [:wallet.send/unsigned-transaction]
symbol [:wallet.send/symbol]
advanced? [:wallet.send/advanced?]] advanced? [:wallet.send/advanced?]]
(if transaction (if transaction
[send-transaction-panel {:modal? true :transaction transaction :advanced? advanced?}] [send-transaction-panel {:modal? true :transaction transaction :advanced? advanced? :symbol symbol}]
[react/view wallet.styles/wallet-modal-container [react/view wallet.styles/wallet-modal-container
[react/view components.styles/flex [react/view components.styles/flex
[status-bar/status-bar {:type :modal-wallet}] [status-bar/status-bar {:type :modal-wallet}]

View File

@ -1,6 +1,7 @@
(ns status-im.utils.ethereum.core (ns status-im.utils.ethereum.core
(:require [clojure.string :as string] (:require [clojure.string :as string]
[status-im.js-dependencies :as dependencies] [status-im.js-dependencies :as dependencies]
[status-im.utils.ethereum.tokens :as tokens]
[status-im.utils.money :as money])) [status-im.utils.money :as money]))
;; IDs standardized in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md#list-of-chain-ids ;; IDs standardized in https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md#list-of-chain-ids
@ -83,3 +84,9 @@
(def default-transaction-gas (money/bignumber 21000)) (def default-transaction-gas (money/bignumber 21000))
(def default-gas-price (money/->wei :gwei 21)) (def default-gas-price (money/->wei :gwei 21))
(defn estimate-gas [symbol]
(if (tokens/ethereum? symbol)
default-transaction-gas
;; TODO(jeluard) Rely on estimateGas call
(.times default-transaction-gas 5)))

View File

@ -23,21 +23,23 @@
(def key-value-format (str "([^" parameter-separator key-value-separator "]+)")) (def key-value-format (str "([^" parameter-separator key-value-separator "]+)"))
(def query-pattern (re-pattern (str key-value-format key-value-separator key-value-format))) (def query-pattern (re-pattern (str key-value-format key-value-separator key-value-format)))
(def valid-native-arguments #{:value :gas}) (def valid-native-arguments #{:value :gas :gasPrice})
(defn- parse-query [s] (defn- parse-query [s]
(into {} (for [[_ k v] (re-seq query-pattern (or s ""))] (into {} (for [[_ k v] (re-seq query-pattern (or s ""))]
[(keyword k) v]))) [(keyword k) v])))
(defn- parse-native-arguments [m] (defn- parse-native-arguments [m]
(when (set/superset? valid-native-arguments (set (keys m))) (select-keys m valid-native-arguments))
m))
(defn- parse-arguments [function-name s] (defn- parse-arguments [function-name s]
(let [m (parse-query s)] (let [m (parse-query s)
arguments (parse-native-arguments m)]
(if function-name (if function-name
(merge {:function-name function-name} (when-not (empty? m) {:function-arguments m})) (merge arguments {:function-name function-name}
(parse-native-arguments m)))) (when (seq m)
{:function-arguments (apply dissoc m valid-native-arguments)}))
arguments)))
;; TODO add ENS support ;; TODO add ENS support
@ -94,5 +96,16 @@
(str chain-id-separator chain-id)) (str chain-id-separator chain-id))
(when-not (empty? parameters) (when-not (empty? parameters)
(if function-name (if function-name
(str function-name-separator function-name query-separator (generate-query-string function-arguments)) (str function-name-separator function-name query-separator
(let [native-parameters (dissoc parameters :function-name :function-arguments)]
(generate-query-string (merge function-arguments native-parameters))))
(str query-separator (generate-query-string parameters)))))))) (str query-separator (generate-query-string parameters))))))))
(defn generate-erc20-uri
"Generate a EIP 681 URI encapsulating ERC20 token transfer"
[address {:keys [symbol value chain-id] :as m}]
(when-let [token (tokens/symbol->token (or (ethereum/chain-id->chain-keyword chain-id) :mainnet) symbol)]
(generate-uri (:address token)
(merge (dissoc m :value :symbol)
{:function-name "transfer"
:function-arguments {:uint256 value :address address}}))))

View File

@ -1,6 +1,5 @@
(ns status-im.utils.ethereum.tokens (ns status-im.utils.ethereum.tokens
(:require [status-im.ui.components.styles :as styles] (:require [status-im.ui.components.styles :as styles])
[status-im.utils.ethereum.core :as ethereum])
(:require-macros [status-im.utils.ethereum.macros :refer [resolve-icons]])) (:require-macros [status-im.utils.ethereum.macros :refer [resolve-icons]]))
(defn- asset-border [color] (defn- asset-border [color]

View File

@ -13,8 +13,8 @@
(is (= nil(eip681/parse-uri "ethereum:0x1234"))) (is (= nil(eip681/parse-uri "ethereum:0x1234")))
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7")))
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1")))
(is (= nil (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?unknown=1"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7", :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?unknown=1")))
(is (= nil (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7", :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7")))
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "2.014e18" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=2.014e18"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "2.014e18" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=2.014e18")))
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "-1e18" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=-1e18"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "-1e18" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=-1e18")))
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "+1E18" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=+1E18"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "+1E18" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=+1E18")))
@ -24,7 +24,21 @@
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1e18" :gas "5000" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@1?value=1e18&gas=5000"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1e18" :gas "5000" :chain-id 1} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@1?value=1e18&gas=5000")))
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1e18" :gas "5000" :chain-id 3} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3?value=1e18&gas=5000"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :value "1e18" :gas "5000" :chain-id 3} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3?value=1e18&gas=5000")))
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1 :function-name "transfer"} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer"))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1 :function-name "transfer"} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer")))
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1 :function-name "transfer" :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 "1"}} (eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1")))) (is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1 :function-name "transfer" :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 "1"}}
(eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1")))
(is (= {:address "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" :chain-id 1 :function-name "transfer" :gas "100" :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 "1"}}
(eip681/parse-uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1&gas=100"))))
(deftest generate-erc20-uri
(is (= nil (eip681/generate-erc20-uri nil nil)))
(is (= "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
(eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :value 5})))
(is (= "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7&gas=10000&gasPrice=10000"
(eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :value 5 :gas 10000 :gasPrice 10000})))
(is (= "ethereum:0x744d70fdbe2ba4cf95131626614a1763df805b9e/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
(eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :SNT :chain-id 1 :value 5})))
(is (= "ethereum:0xc55cf4b03948d7ebc8b9e8bad92643703811d162@3/transfer?uint256=5&address=0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
(eip681/generate-erc20-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:symbol :STT :chain-id 3 :value 5}))))
(deftest generate-uri (deftest generate-uri
(is (= nil (eip681/generate-uri nil nil))) (is (= nil (eip681/generate-uri nil nil)))
@ -35,7 +49,18 @@
(is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1000000000000000000" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1e18)}))) (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1000000000000000000" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1e18)})))
(is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1&gas=100" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 1}))) (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7?value=1&gas=100" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 1})))
(is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3?value=1&gas=100" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 3}))) (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3?value=1&gas=100" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 3})))
(is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1" (eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 1 :function-name "transfer" :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 1}})))) (is (= "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7/transfer?address=0x8e23ee67d1332ad560396262c48ffbb01f93d052&uint256=1&gas=100"
(eip681/generate-uri "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7"
{:gas (money/bignumber 100) :chain-id 1 :function-name "transfer" :function-arguments {:address "0x8e23ee67d1332ad560396262c48ffbb01f93d052" :uint256 1}}))))
(deftest round-trip
(let [uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3?value=1&gas=100"
{:keys [address] :as params} (eip681/parse-uri uri)]
(is (= uri (eip681/generate-uri address (dissoc params :address)))))
(let [uri "ethereum:0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7@3/transfer?uint256=5&address=0xc55cf4b03948d7ebc8b9e8bad92643703811d162"
{:keys [address] :as params} (eip681/parse-uri uri)]
(is (= uri (eip681/generate-uri address (dissoc params :address))))))
(deftest parse-eth-value (deftest parse-eth-value
(is (= nil (eip681/parse-eth-value nil))) (is (= nil (eip681/parse-eth-value nil)))