[ISSUE #2391] Added support for ERC681

This commit is contained in:
Julien Eluard 2017-11-11 08:19:29 +01:00 committed by Oskar Thorén
parent 63848efc20
commit 7e6507be7c
26 changed files with 243 additions and 181 deletions

View File

@ -5,15 +5,15 @@
[reagent.core :as r]
[status-im.i18n :refer [message-status-label]]
[status-im.ui.components.react :refer [view
text
image
icon
animated-view
touchable-without-feedback
touchable-highlight
autolink
get-dimensions
dismiss-keyboard!]]
text
image
icon
animated-view
touchable-without-feedback
touchable-highlight
autolink
get-dimensions
dismiss-keyboard!]]
[status-im.ui.components.animation :as anim]
[status-im.ui.components.list-selection :refer [share share-or-open-map]]
[status-im.chat.constants :as chat-consts]

View File

@ -336,6 +336,7 @@
:wallet-choose-from-contacts "Choose From Contacts"
:wallet-address-from-clipboard "Use Address From Clipboard"
:wallet-invalid-address "Invalid address: \n {{data}}"
:wallet-invalid-chain-id "Network does not match: \n {{data}}"
:wallet-browse-photos "Browse Photos"
:validation-amount-invalid-number "Amount is not a valid number"
:validation-amount-is-too-precise "Amount is too precise. The smallest unit you can send is 1 Wei (1x10^-18 ETH)"

View File

@ -1,6 +1,6 @@
(ns status-im.ui.components.chat-icon.styles
(:require [status-im.ui.components.styles :refer [color-white
online-color]]
online-color]]
[status-im.utils.platform :as p]))
(defn default-chat-icon [color]

View File

@ -9,15 +9,15 @@
[status-im.ui.components.context-menu :as context-menu]
[status-im.ui.components.drawer.styles :as st]
[status-im.ui.components.react :refer [view
text
text-input
list-item
list-view
drawer-layout
touchable-without-feedback
touchable-highlight
touchable-opacity
dismiss-keyboard!]]
text
text-input
list-item
list-view
drawer-layout
touchable-without-feedback
touchable-highlight
touchable-opacity
dismiss-keyboard!]]
[status-im.ui.components.icons.vector-icons :as vi]
[status-im.ui.components.status-view.view :as status-view]
[status-im.i18n :as i18n]

View File

@ -1,8 +1,8 @@
(ns status-im.ui.components.list-selection
(:require [re-frame.core :refer [dispatch]]
[status-im.ui.components.react :refer [copy-to-clipboard
sharing
linking]]
sharing
linking]]
[status-im.utils.platform :refer [platform-specific ios?]]
[status-im.i18n :refer [label]]))

View File

@ -2,9 +2,9 @@
(:require [re-frame.core :refer [subscribe dispatch]]
[reagent.core :as r]
[status-im.ui.components.react :refer [view
text
animated-view
get-dimensions]]
text
animated-view
get-dimensions]]
[status-im.ui.components.sync-state.styles :as st]
[status-im.ui.components.animation :as anim]
[status-im.i18n :refer [label]]))

View File

@ -7,13 +7,15 @@
[status-im.ui.components.status-bar :refer [status-bar]]
[status-im.i18n :refer [label]]
[status-im.ui.screens.profile.qr-code.styles :as styles]
[status-im.utils.eip.eip67 :as eip67])
[status-im.utils.money :as money]
[status-im.utils.eip.eip681 :as eip681])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
(defview qr-code-view []
(letsubs [{:keys [photo-path address name]} [:get-in [:qr-modal :contact]]
{:keys [qr-source qr-value amount? dimensions]} [:get :qr-modal]
{:keys [amount]} [:get :contacts/click-params]]
{:keys [amount]} [:get :contacts/click-params]
chain-id [:get-network-id]]
[react/view styles/wallet-qr-code
[status-bar {:type :modal}]
[react/view styles/account-toolbar
@ -35,7 +37,7 @@
:height (.-height layout)}]))}
(when (:width dimensions)
[react/view {:style (styles/qr-code-container dimensions)}
(when-let [value (eip67/generate-uri qr-value (when amount? {:value amount}))]
(when-let [value (eip681/generate-uri qr-value (merge {:chain-id chain-id} (when amount? {:value (money/str->wei amount)})))]
[qr-code {:value value
:size (- (min (:width dimensions)
(:height dimensions))

View File

@ -5,7 +5,7 @@
[status-im.utils.handlers :as u :refer [register-handler]]
[status-im.utils.utils :as utils]
[status-im.i18n :as i18n]
[status-im.utils.eip.eip67 :as eip67]))
[status-im.utils.eip.eip681 :as eip681]))
(defmethod nav/preload-data! :qr-scanner
[db [_ _ identifier]]
@ -35,7 +35,7 @@
(defn handle-qr-request
[db [_ context data]]
(when-let [handler (get-in db [:qr-codes context])]
(re-frame/dispatch [handler context (:address (eip67/parse-uri data))])))
(re-frame/dispatch [handler context (:address (eip681/parse-uri data))])))
(defn clear-qr-request [db [_ context]]
(-> db

View File

@ -1,6 +1,7 @@
(ns status-im.ui.screens.wallet.choose-recipient.events
(:require [status-im.i18n :as i18n]
[status-im.utils.eip.eip67 :as eip67]
(:require [status-im.constants :as constants]
[status-im.i18n :as i18n]
[status-im.utils.eip.eip681 :as eip681]
[status-im.utils.handlers :as handlers]))
(handlers/register-handler-db
@ -17,22 +18,25 @@
amount (assoc :amount amount))))
(defn- extract-details
"First try to parse as EIP67 URI, if not assume this is an address directly.
Returns a map containing at least the `address` key"
[s]
(or (eip67/parse-uri s) {:address s}))
"First try to parse as EIP681 URI, if not assume this is an address directly.
Returns a map containing at least the `address` and `chain-id` keys"
[s network]
(or (eip681/parse-uri s) {:address s :chain-id network}))
(handlers/register-handler-fx
:choose-recipient
(fn [{{:keys [web3] :as db} :db} [_ data name]]
(let [{:keys [view-id]} db
m (extract-details data)
address (:address m)
(fn [{{:keys [web3 network] :as db} :db} [_ data name]]
(let [{:keys [view-id]} db
current-network-id (get-in constants/default-networks [network :raw-config :NetworkId])
{:keys [address chain-id] :as m} (extract-details data current-network-id)
;; isAddress works with or without address with leading '0x'
valid-address? (.isAddress web3 address)]
valid-address? (.isAddress web3 address)
valid-network? (boolean (= current-network-id chain-id))]
(cond-> {:db db}
valid-address? (update :db #(choose-address-and-name % address name (:value m)))
(not valid-address?) (assoc :show-error (i18n/label :t/wallet-invalid-address {:data data}))))))
(and valid-address? (= :choose-recipient view-id)) (assoc :dispatch [:navigate-back])
(and valid-network? valid-address?) (update :db #(choose-address-and-name % address name (eip681/parse-value m)))
(not valid-address?) (assoc :show-error (i18n/label :t/wallet-invalid-address {:data data}))
(and valid-address? (not valid-network?)) (assoc :show-error (i18n/label :t/wallet-invalid-chain-id {:data data}))))))
(handlers/register-handler-fx
:wallet-open-send-transaction

View File

@ -41,7 +41,7 @@
[react/touchable-highlight {:style (styles/recipient-touchable true)
:on-press #(react/get-from-clipboard
(fn [clipboard]
(re-frame/dispatch [:choose-recipient (string/trim clipboard) nil])))}
(re-frame/dispatch [:choose-recipient (string/trim-newline clipboard) nil])))}
[react/view {:style styles/recipient-button}
[react/text {:style styles/recipient-button-text}
(i18n/label :t/wallet-address-from-clipboard)]

View File

@ -29,12 +29,16 @@
(let [amount-splited (string/split amount #"[.]")]
(and (= (count amount-splited) 2) (> (count (last amount-splited)) 18))))
(defn get-amount-validation-error [amount]
(defn parse-amount [amount]
(when-not (empty-amount? amount)
(let [normalized-amount (money/normalize amount)]
(let [normalized-amount (money/normalize amount)
value (money/bignumber normalized-amount)]
(cond
(not (money/valid? normalized-amount))
(i18n/label :t/validation-amount-invalid-number)
(not (money/valid? value))
{:error (i18n/label :t/validation-amount-invalid-number)}
(too-precise-amount? normalized-amount)
(i18n/label :t/validation-amount-is-too-precise)))))
{:error (i18n/label :t/validation-amount-is-too-precise)}
:else
{:value value}))))

View File

@ -1,8 +1,9 @@
(ns status-im.ui.screens.wallet.request.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec]))
(:require [cljs.spec.alpha :as spec]
[status-im.utils.money :as money]))
(spec/def ::amount (spec/nilable string?))
(spec/def ::amount (spec/nilable money/valid?))
(spec/def ::amount-error (spec/nilable string?))
(spec/def :wallet/request-transaction (allowed-keys

View File

@ -1,8 +1,8 @@
(ns status-im.ui.screens.wallet.request.events
(:require
[status-im.utils.handlers :as handlers]
[status-im.ui.screens.wallet.db :as wallet.db]
[re-frame.core :as re-frame]))
(:require [re-frame.core :as re-frame]
[status-im.ui.screens.wallet.db :as wallet.db]
[status-im.utils.handlers :as handlers]
[status-im.utils.money :as money]))
(handlers/register-handler-fx
::wallet-send-chat-request
@ -16,12 +16,13 @@
(fn [{{:wallet/keys [request-transaction]} :db} [_ {:keys [whisper-identity]}]]
{:dispatch-n [[:navigate-back]
[:navigate-to-clean :chat-list]
[:add-chat-loaded-event whisper-identity [::wallet-send-chat-request (:amount request-transaction)]]
[:add-chat-loaded-event whisper-identity [::wallet-send-chat-request (str (:amount request-transaction))]]
[:start-chat whisper-identity]]}))
(handlers/register-handler-fx
:wallet-validate-request-amount
(fn [{{:wallet/keys [request-transaction] :as db} :db} _]
(let [amount (:amount request-transaction)
error (wallet.db/get-amount-validation-error amount)]
{:db (assoc-in db [:wallet/request-transaction :amount-error] error)})))
:wallet.request/set-and-validate-amount
(fn [{:keys [db]} [_ amount]]
(let [{:keys [value error]} (wallet.db/parse-amount amount)]
{:db (-> db
(assoc-in [:wallet/request-transaction :amount] (money/ether->wei value))
(assoc-in [:wallet/request-transaction :amount-error] error))})))

View File

@ -8,4 +8,4 @@
(fn [[amount amount-error]]
(and
(nil? amount-error)
(not (nil? amount)) (not= amount ""))))
(not (nil? amount)))))

View File

@ -8,14 +8,15 @@
[status-im.ui.components.toolbar.view :as toolbar]
[status-im.ui.components.status-bar :as status-bar]
[status-im.ui.screens.wallet.styles :as wallet.styles]
[status-im.ui.components.common.common :as common]
[status-im.ui.components.icons.vector-icons :as vi]
[status-im.ui.screens.wallet.components.views :as components]
[status-im.ui.screens.wallet.request.styles :as styles]
[status-im.ui.components.styles :as components.styles]
[status-im.i18n :as i18n]
[status-im.utils.platform :as platform]
[status-im.utils.eip.eip67 :as eip67]
[status-im.ui.components.common.common :as common]))
[status-im.utils.eip.eip681 :as eip681]
[status-im.utils.money :as money]))
(defn toolbar-view []
[toolbar/toolbar {:style wallet.styles/toolbar :hide-border? true}
@ -30,9 +31,10 @@
:params {:hide-actions? true}}]))
(views/defview qr-code [amount]
(views/letsubs [account [:get-current-account]]
(views/letsubs [account [:get-current-account]
chain-id [:get-network-id]]
[components.qr-code/qr-code
{:value (eip67/generate-uri (:address account) (when amount {:value amount}))
{:value (eip681/generate-uri (:address account) (merge {:chain-id chain-id} (when amount {:value amount})))
:size 256}]))
(views/defview request-transaction []
@ -56,9 +58,7 @@
[components/amount-input
{:error amount-error
:input-options {:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100)))
:on-change-text
#(do (re-frame/dispatch [:set-in [:wallet/request-transaction :amount] %])
(re-frame/dispatch [:wallet-validate-request-amount]))}}]
:on-change-text #(re-frame/dispatch [:wallet.request/set-and-validate-amount %])}}]
[react/view wallet.styles/choose-currency-container
[components/choose-currency wallet.styles/choose-currency]]]]]
[components/separator]
@ -68,7 +68,7 @@
[vi/icon :icons/share {:color :white :container-style styles/share-icon-container}]
[components/button-text (i18n/label :t/share)]]]
[react/view components.styles/flex]
[react/touchable-highlight {:style wallet.styles/button :disabled (not request-enabled?) :on-press send-request}
[react/touchable-highlight {:style wallet.styles/button :disabled (not request-enabled?) :on-press send-request}
[react/view (wallet.styles/button-container request-enabled?)
[components/button-text (i18n/label :t/send-request)]
[vi/icon :icons/forward {:color :white :container-style wallet.styles/forward-icon-container}]]]]]))

View File

@ -1,8 +1,9 @@
(ns status-im.ui.screens.wallet.send.db
(:require-macros [status-im.utils.db :refer [allowed-keys]])
(:require [cljs.spec.alpha :as spec]))
(:require [cljs.spec.alpha :as spec]
[status-im.utils.money :as money]))
(spec/def ::amount (spec/nilable string?))
(spec/def ::amount (spec/nilable money/valid?))
(spec/def ::to-address (spec/nilable string?))
(spec/def ::to-name (spec/nilable string?))
(spec/def ::amount-error (spec/nilable string?))

View File

@ -52,11 +52,11 @@
;;;; Handlers
(handlers/register-handler-fx
:wallet/set-and-validate-amount
:wallet.send/set-and-validate-amount
(fn [{:keys [db]} [_ amount]]
(let [error (wallet.db/get-amount-validation-error amount)]
(let [{:keys [value error]} (wallet.db/parse-amount amount)]
{:db (-> db
(assoc-in [:wallet :send-transaction :amount] amount)
(assoc-in [:wallet :send-transaction :amount] (money/ether->wei value))
(assoc-in [:wallet :send-transaction :amount-error] error))})))
(def ^:private clear-send-properties {:id nil
@ -174,7 +174,7 @@
::send-transaction {:web3 web3
:from (get-in accounts [current-account-id :address])
:to to-address
:value (money/to-wei (money/normalize amount))}}))))
:value amount}}))))
(handlers/register-handler-fx
:wallet/sign-transaction-modal

View File

@ -75,9 +75,8 @@
(fn [[{:keys [value to] :as transaction} contacts balance]]
(when transaction
(let [contact (contacts (utils.hex/normalize-hex to))
amount (str (.toFixed (money/wei->ether value)))
sufficient-funds? (money/sufficient-funds? amount balance)]
sufficient-funds? (money/sufficient-funds? value balance)]
(cond-> (assoc transaction
:amount amount
:amount value
:sufficient-funds? sufficient-funds?)
contact (assoc :to-name (:name contact)))))))

View File

@ -76,7 +76,7 @@
(and
(nil? amount-error)
(not (nil? to-address)) (not= to-address "")
(not (nil? amount)) (not= amount "")))
(not (nil? amount))))
;; "Sign Later" and "Sign Transaction >" buttons
(defn- sign-buttons [amount-error to-address amount sufficient-funds? sign-later-handler]
@ -126,8 +126,8 @@
(when-not sufficient-funds? (i18n/label :t/wallet-insufficient-funds)))
:input-options {:auto-focus true
:on-focus (fn [] (when @scroll (js/setTimeout #(.scrollToEnd @scroll) 100)))
:default-value amount
:on-change-text #(re-frame/dispatch [:wallet/set-and-validate-amount %])}}]
:default-value (str (money/wei->ether amount))
:on-change-text #(re-frame/dispatch [:wallet.send/set-and-validate-amount %])}}]
[react/view wallet.styles/choose-currency-container
[components/choose-currency wallet.styles/choose-currency]]]]]
[components/separator]
@ -168,7 +168,7 @@
[components/amount-input
{:error (when-not sufficient-funds? (i18n/label :t/wallet-insufficient-funds))
:disabled? true
:input-options {:default-value amount}}]
:input-options {:default-value (str (money/wei->ether amount))}}]
[react/view wallet.styles/choose-currency-container
[components/choose-currency wallet.styles/choose-currency]]]]]
[components/separator]

View File

@ -1,44 +0,0 @@
(ns status-im.utils.eip.eip67
"Utility function related to [EIP67](https://github.com/ethereum/EIPs/issues/67)"
(:require [clojure.string :as string]))
(def scheme "ethereum")
(def scheme-separator ":")
(def parameters-separator "?")
(def parameter-separator "&")
(def key-value-separator "=")
(def key-value-format (str "([^" parameter-separator key-value-separator "]+)"))
(def parameters-pattern (re-pattern (str key-value-format key-value-separator key-value-format)))
(defn- parse-parameters [s]
(when s
(into {} (for [[_ k v] (re-seq parameters-pattern s)]
[(keyword k) v]))))
(defn parse-uri
"Parse a EIP 67 URI as a map of keyword / strings. Parsed map will contain at least the key `address`.
Invalid URI will be parsed as `nil`."
[s]
(when (and s (string/starts-with? s scheme))
(let [[address parameters] (string/split (string/replace s (str scheme scheme-separator) "") parameters-separator)]
(when-not (zero? (count address))
(merge
{:address address}
(parse-parameters parameters))))))
(defn- generate-parameter-string [m]
(string/join parameter-separator (for [[k v] m]
(str (name k) key-value-separator v))))
(defn generate-uri
"Generate a EIP 67 URI based on `address` and an optional map (keyword, string) of extra properties.
No validation of address format is performed."
([address] (generate-uri address nil))
([address m]
(when address
(let [parameters (into {} (filter second m))] ;; filter nil values
(str scheme scheme-separator address
(when-not (empty? parameters)
(str parameters-separator (generate-parameter-string parameters))))))))

View File

@ -0,0 +1,66 @@
(ns status-im.utils.eip.eip681
"Utility function related to [EIP681](https://github.com/ethereum/EIPs/issues/681)
This EIP standardize how ethereum payment request can be represented as URI (say to embed them in a QR code).
e.g. ethereum:0x1234@1/transfer?to=0x5678&value=1e18&gas=5000"
(:require [clojure.string :as string]
[status-im.constants :as constants]
[status-im.utils.money :as money]))
(def scheme "ethereum")
(def scheme-separator ":")
(def chain-id-separator "@")
(def function-name-separator "/")
(def query-separator "?")
(def parameter-separator "&")
(def key-value-separator "=")
(def uri-pattern (re-pattern (str scheme scheme-separator "([^" query-separator "]*)(?:\\" query-separator "(.*))?")))
(def authority-path-pattern (re-pattern (str "^([^" chain-id-separator "]*)(?:" chain-id-separator "(\\d))?(?:" function-name-separator "(\\w*))?")))
(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)))
(defn- parse-query [s]
(when s
(into {} (for [[_ k v] (re-seq query-pattern s)]
[(keyword k) v]))))
(defn parse-value [{:keys [value function-name]}]
"Takes a map as returned by `parse-uri` and returns value as BigNumber"
(when (and value (not function-name)) ;; TODO(jeluard) Add ERC20 support
(let [eth? (string/ends-with? value "ETH")
n (money/bignumber (string/replace value "ETH" ""))]
(if eth? (.times n 1e18) n))))
(defn parse-uri
"Parse a EIP 681 URI as a map (keyword / strings). Parsed map will contain at least the key `address`.
Note that values are not decoded and you might need to rely on specific methods for some fields (parse-value, parse-number).
Invalid URI will be parsed as `nil`."
[s]
(when (string? s)
(let [[_ authority-path query] (re-find uri-pattern s)]
(when authority-path
(let [[_ address chain-id function-name] (re-find authority-path-pattern authority-path)]
(when-not (or (string/blank? address) function-name) ;; Native token support only TODO(jeluard) Add ERC20 support
(merge {:address address :chain-id (if chain-id (js/parseInt chain-id) constants/mainnet-id)}
(parse-query query))))))))
(defn- generate-query-string [m]
(string/join parameter-separator
(for [[k v] m]
(str (name k) key-value-separator v))))
(defn generate-uri
"Generate a EIP 681 URI based on `address` and a map (keywords / {bignumbers/strings} ) of extra properties.
No validation of address format is performed."
[address {:keys [function-name chain-id] :as m}]
(when (and address (not function-name)) ;; Native token support only TODO(jeluard) Add ERC20 support
(let [parameters (dissoc (into {} (filter second m)) :chain-id)] ;; filter nil values
(str scheme scheme-separator address
(when (and chain-id (not= chain-id constants/mainnet-id))
;; Add chain-id if specified and is not main-net
(str chain-id-separator chain-id))
(when-not (empty? parameters)
(str query-separator (generate-query-string parameters)))))))

View File

@ -22,32 +22,31 @@
(defn normalize
"A normalized string representation of an amount"
[str]
{:pre [(or (nil? str) (string? str))]}
(when str
(string/replace (string/trim str) #"," ".")))
[s]
{:pre [(or (nil? s) (string? s))]}
(when s
(string/replace (string/trim s) #"," ".")))
(defn bignumber [n]
(when str
(when n
(try
(dependencies/Web3.prototype.toBigNumber (str n))
(catch :default err nil))))
(defn valid? [str]
(when str
(when-let [bn (bignumber (normalize str))]
(.greaterThanOrEqualTo bn 0))))
(defn valid? [bn]
(when bn
(.greaterThanOrEqualTo bn 0)))
(defn to-wei [str]
(when str
(defn str->wei [s]
(when-let [ns (normalize s)]
(try
(dependencies/Web3.prototype.toWei (normalize str) "ether")
(dependencies/Web3.prototype.toWei ns "ether")
(catch :default err nil))))
(defn to-decimal [str]
(when str
(defn to-decimal [s]
(when s
(try
(dependencies/Web3.prototype.toDecimal (normalize str))
(dependencies/Web3.prototype.toDecimal (normalize s))
(catch :default err nil))))
(def eth-units
@ -64,10 +63,12 @@
:teth (bignumber "1000000000000000000000000000000")})
(defn wei-> [unit n]
(.dividedBy (bignumber n) (eth-units unit)))
(when-let [bn (bignumber n)]
(.dividedBy bn (eth-units unit))))
(defn to-fixed [bn]
(.toFixed bn))
(when bn
(.toFixed bn)))
(defn wei->str [unit n]
(str (to-fixed (wei-> unit n)) " " (string/upper-case (name unit))))
@ -75,6 +76,10 @@
(defn wei->ether [n]
(wei-> :eth n))
(defn ether->wei [bn]
(when bn
(.times bn (bignumber 1e18))))
(defn fee-value [gas gas-price]
(.times (bignumber gas) (bignumber gas-price)))
@ -82,12 +87,17 @@
(.times (bignumber eth) (bignumber usd-price)))
(defn percent-change [from to]
(-> (.dividedBy (bignumber from) (bignumber to))
(.minus 1)
(.times 100)))
(let [bnf (bignumber from)
bnt (bignumber to)]
(when (and bnf bnt)
(-> (.dividedBy bnf bnt)
(.minus 1)
(.times 100)))))
(defn with-precision [n decimals]
(.round (bignumber n) decimals))
(when-let [bn (bignumber n)]
(.round bn decimals)))
(defn sufficient-funds? [amount-in-eth balance]
(.greaterThanOrEqualTo balance (bignumber (to-wei amount-in-eth))))
(defn sufficient-funds? [amount balance]
(when amount
(.greaterThanOrEqualTo balance amount)))

View File

@ -12,7 +12,7 @@
[status-im.test.utils.utils]
[status-im.test.utils.money]
[status-im.test.utils.clocks]
[status-im.test.utils.eip.eip67]
[status-im.test.utils.eip.eip681]
[status-im.test.utils.erc20]
[status-im.test.utils.random]
[status-im.test.utils.gfycat.core]
@ -42,7 +42,7 @@
'status-im.test.utils.utils
'status-im.test.utils.money
'status-im.test.utils.clocks
'status-im.test.utils.eip.eip67
'status-im.test.utils.eip.eip681
'status-im.test.utils.erc20
'status-im.test.utils.random
'status-im.test.utils.gfycat.core

View File

@ -1,21 +0,0 @@
(ns status-im.test.utils.eip.eip67
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.eip.eip67 :as eip67]))
(deftest parse-uri
(is (= nil (eip67/parse-uri nil)))
(is (= nil (eip67/parse-uri "random")))
(is (= nil (eip67/parse-uri "ethereum:")))
(is (= nil (eip67/parse-uri "ethereum:?value=1")))
(is (= nil (eip67/parse-uri "bitcoin:0x1234")))
(is (= {:address "0x1234"} (eip67/parse-uri "ethereum:0x1234")))
(is (= {:address "0x1234" :to "0x5678" :value "1"} (eip67/parse-uri "ethereum:0x1234?to=0x5678&value=1"))))
(deftest generate-uri
(is (= nil (eip67/generate-uri nil)))
(is (= "ethereum:0x1234" (eip67/generate-uri "0x1234")))
(is (= "ethereum:0x1234" (eip67/generate-uri "0x1234" nil)))
(is (= "ethereum:0x1234" (eip67/generate-uri "0x1234" {})))
(is (= "ethereum:0x1234" (eip67/generate-uri "0x1234" {:to nil})))
(is (= "ethereum:0x1234?to=0x5678" (eip67/generate-uri "0x1234" {:to "0x5678"})))
(is (= "ethereum:0x1234?to=0x5678&value=1" (eip67/generate-uri "0x1234" {:to "0x5678" :value 1}))))

View File

@ -0,0 +1,44 @@
(ns status-im.test.utils.eip.eip681
(:require [cljs.test :refer-macros [deftest is testing]]
[status-im.utils.eip.eip681 :as eip681]
[status-im.utils.money :as money]))
(deftest parse-uri
(is (= nil (eip681/parse-uri nil)))
(is (= nil (eip681/parse-uri 5)))
(is (= nil (eip681/parse-uri "random")))
(is (= nil (eip681/parse-uri "ethereum:")))
(is (= nil (eip681/parse-uri "ethereum:?value=1")))
(is (= nil (eip681/parse-uri "bitcoin:0x1234")))
(is (= {:address "0x1234" :chain-id 1} (eip681/parse-uri "ethereum:0x1234")))
(is (= {:address "0x1234" :value "1" :chain-id 1} (eip681/parse-uri "ethereum:0x1234?value=1")))
(is (= {:address "0x1234" :value "-1e18" :chain-id 1} (eip681/parse-uri "ethereum:0x1234?value=-1e18")))
(is (= {:address "0x1234" :value "+1E18" :chain-id 1} (eip681/parse-uri "ethereum:0x1234?value=+1E18")))
(is (= {:address "0x1234" :value "1E18" :gas "100" :chain-id 1} (eip681/parse-uri "ethereum:0x1234?value=1E18&gas=100")))
(is (= {:address "0x1234" :value "NOT_NUMBER" :chain-id 1} (eip681/parse-uri "ethereum:0x1234?value=NOT_NUMBER")))
(is (= {:address "0x1234" :value "1ETH" :chain-id 1} (eip681/parse-uri "ethereum:0x1234?value=1ETH")))
(is (= {:address "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" :value "1e18" :gas "5000" :chain-id 1} (eip681/parse-uri "ethereum:0xadaf150b905cf5e6a778e553e15a139b6618bbb7@1?value=1e18&gas=5000")))
(is (= {:address "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" :value "1e18" :gas "5000" :chain-id 3} (eip681/parse-uri "ethereum:0xadaf150b905cf5e6a778e553e15a139b6618bbb7@3?value=1e18&gas=5000")))
(is (= nil (eip681/parse-uri "ethereum:0xadaf150b905cf5e6a778e553e15a139b6618bbb7@2/transfer?value=1e18&gas=5000"))))
(deftest generate-uri
(is (= nil (eip681/generate-uri nil nil)))
(is (= "ethereum:0x1234" (eip681/generate-uri "0x1234" nil)))
(is (= "ethereum:0x1234" (eip681/generate-uri "0x1234" {})))
(is (= "ethereum:0x1234" (eip681/generate-uri "0x1234" {:value nil})))
(is (= "ethereum:0xadaf150b905cf5e6a778e553e15a139b6618bbb7?value=1" (eip681/generate-uri "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" {:value (money/bignumber 1)})))
(is (= "ethereum:0xadaf150b905cf5e6a778e553e15a139b6618bbb7?value=1000000000000000000" (eip681/generate-uri "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" {:value (money/bignumber 1e18)})))
(is (= "ethereum:0xadaf150b905cf5e6a778e553e15a139b6618bbb7?value=1&gas=100" (eip681/generate-uri "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 1})))
(is (= "ethereum:0xadaf150b905cf5e6a778e553e15a139b6618bbb7@3?value=1&gas=100" (eip681/generate-uri "0xadaf150b905cf5e6a778e553e15a139b6618bbb7" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 3})))
(is (= nil (eip681/generate-uri "0x1234" {:value (money/bignumber 1) :gas (money/bignumber 100) :chain-id 1 :function-name "transfer"}))))
(deftest parse-value
(is (= nil (eip681/parse-value nil)))
(is (= nil (eip681/parse-value 1)))
(is (= nil (eip681/parse-value {:value "NOT_NUMBER"})))
(is (= nil (eip681/parse-value {:value "1" :function-name "transfer"})))
(is (.equals (money/bignumber 1) (eip681/parse-value {:value "1"})))
(is (.equals (money/bignumber 1e18) (eip681/parse-value {:value "1ETH"})))
(is (.equals (money/bignumber -1e18) (eip681/parse-value {:value "-1e18"})))
(is (.equals (money/bignumber 1e18) (eip681/parse-value {:value "1E18"})))
(is (.equals (money/bignumber "111122223333441239") (eip681/parse-value {:value "111122223333441239"}))))

View File

@ -12,15 +12,9 @@
(deftest valid?
(is (not (true? (money/valid? nil))))
(is (not (true? (money/valid? "a"))))
(is (not (true? (money/valid? "-1"))))
(is (not (true? (money/valid? "1a"))))
(is (not (true? (money/valid? "0,,"))))
(is (true? (money/valid? "1")))
(is (true? (money/valid? "1.1")))
(is (true? (money/valid? "1,1")))
(is (true? (money/valid? "0.00000000000000000000001")))
(is (true? (money/valid? "0.0000000000000000000000000001"))))
(is (true? (money/valid? (money/bignumber 0))))
(is (true? (money/valid? (money/bignumber 1))))
(is (not (true? (money/valid? (money/bignumber -1))))))
(deftest normalize
(is (= nil (money/normalize nil)))