Complete implementation of the `/send` command + tests
This commit is contained in:
parent
6672400041
commit
03598d47c2
|
@ -1,14 +1,21 @@
|
||||||
(ns status-im.chat.commands.impl.transactions
|
(ns status-im.chat.commands.impl.transactions
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]])
|
(: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.chat.commands.protocol :as protocol]
|
[status-im.chat.commands.protocol :as protocol]
|
||||||
|
[status-im.chat.commands.impl.transactions.styles :as transactions-styles]
|
||||||
|
[status-im.chat.events.requests :as request-events]
|
||||||
[status-im.ui.components.react :as react]
|
[status-im.ui.components.react :as react]
|
||||||
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
[status-im.ui.components.icons.vector-icons :as vector-icons]
|
||||||
[status-im.ui.components.colors :as colors]
|
[status-im.ui.components.colors :as colors]
|
||||||
[status-im.ui.components.list.views :as list]
|
[status-im.ui.components.list.views :as list]
|
||||||
[status-im.i18n :as i18n]
|
[status-im.i18n :as i18n]
|
||||||
[status-im.chat.commands.impl.transactions.styles :as transactions-styles]
|
[status-im.constants :as constants]
|
||||||
[status-im.chat.styles.message.message :as message-styles]))
|
[status-im.utils.ethereum.core :as ethereum]
|
||||||
|
[status-im.utils.ethereum.tokens :as tokens]
|
||||||
|
[status-im.ui.screens.wallet.send.events :as send.events]
|
||||||
|
[status-im.ui.screens.wallet.choose-recipient.events :as choose-recipient.events]
|
||||||
|
[status-im.ui.screens.navigation :as navigation]))
|
||||||
|
|
||||||
(defn- render-asset [selected-event-creator]
|
(defn- render-asset [selected-event-creator]
|
||||||
(fn [{:keys [name symbol amount decimals] :as asset}]
|
(fn [{:keys [name symbol amount decimals] :as asset}]
|
||||||
|
@ -38,7 +45,7 @@
|
||||||
:keyboardShouldPersistTaps :always
|
:keyboardShouldPersistTaps :always
|
||||||
:bounces false}]]))
|
:bounces false}]]))
|
||||||
|
|
||||||
(defn send-short-preview
|
(defn personal-send-request-short-preview
|
||||||
[{:keys [content]}]
|
[{:keys [content]}]
|
||||||
(let [parameters (:params content)]
|
(let [parameters (:params content)]
|
||||||
[react/text {}
|
[react/text {}
|
||||||
|
@ -52,12 +59,12 @@
|
||||||
tx-exists? [:wallet-transaction-exists? tx-hash]]
|
tx-exists? [:wallet-transaction-exists? tx-hash]]
|
||||||
[react/touchable-highlight {:on-press #(when tx-exists?
|
[react/touchable-highlight {:on-press #(when tx-exists?
|
||||||
(re-frame/dispatch [:show-transaction-details tx-hash]))}
|
(re-frame/dispatch [:show-transaction-details tx-hash]))}
|
||||||
[react/view message-styles/command-send-status-container
|
[react/view transactions-styles/command-send-status-container
|
||||||
[vector-icons/icon (if confirmed? :icons/check :icons/dots)
|
[vector-icons/icon (if confirmed? :icons/check :icons/dots)
|
||||||
{:color colors/blue
|
{:color colors/blue
|
||||||
:container-style (message-styles/command-send-status-icon outgoing)}]
|
:container-style (transactions-styles/command-send-status-icon outgoing)}]
|
||||||
[react/view
|
[react/view
|
||||||
[react/text {:style message-styles/command-send-status-text}
|
[react/text {:style transactions-styles/command-send-status-text}
|
||||||
(i18n/label (cond
|
(i18n/label (cond
|
||||||
confirmed? :status-confirmed
|
confirmed? :status-confirmed
|
||||||
tx-exists? :status-pending
|
tx-exists? :status-pending
|
||||||
|
@ -69,65 +76,159 @@
|
||||||
(let [{{:keys [amount fiat-amount tx-hash asset currency] send-network :network} :params} content
|
(let [{{:keys [amount fiat-amount tx-hash asset currency] send-network :network} :params} content
|
||||||
recipient-name (get-in content [:params :bot-db :public :recipient])
|
recipient-name (get-in content [:params :bot-db :public :recipient])
|
||||||
network-mismatch? (and (seq send-network) (not= network send-network))]
|
network-mismatch? (and (seq send-network) (not= network send-network))]
|
||||||
[react/view message-styles/command-send-message-view
|
[react/view transactions-styles/command-send-message-view
|
||||||
[react/view
|
[react/view
|
||||||
[react/view message-styles/command-send-amount-row
|
[react/view transactions-styles/command-send-amount-row
|
||||||
[react/view message-styles/command-send-amount
|
[react/view transactions-styles/command-send-amount
|
||||||
[react/text {:style message-styles/command-send-amount-text
|
[react/text {:style transactions-styles/command-send-amount-text
|
||||||
:font :medium}
|
:font :medium}
|
||||||
amount
|
amount
|
||||||
[react/text {:style (message-styles/command-amount-currency-separator outgoing)}
|
[react/text {:style (transactions-styles/command-amount-currency-separator outgoing)}
|
||||||
"."]
|
"."]
|
||||||
[react/text {:style (message-styles/command-send-currency-text outgoing)
|
[react/text {:style (transactions-styles/command-send-currency-text outgoing)
|
||||||
:font :default}
|
:font :default}
|
||||||
asset]]]]
|
asset]]]]
|
||||||
(when fiat-amount
|
(when fiat-amount
|
||||||
[react/view message-styles/command-send-fiat-amount
|
[react/view transactions-styles/command-send-fiat-amount
|
||||||
[react/text {:style message-styles/command-send-fiat-amount-text}
|
[react/text {:style transactions-styles/command-send-fiat-amount-text}
|
||||||
(str "~ " fiat-amount " " (or currency (i18n/label :usd-currency)))]])
|
(str "~ " fiat-amount " " (or currency (i18n/label :usd-currency)))]])
|
||||||
(when (and group-chat
|
(when (and group-chat
|
||||||
recipient-name)
|
recipient-name)
|
||||||
[react/text {:style message-styles/command-send-recipient-text}
|
[react/text {:style transactions-styles/command-send-recipient-text}
|
||||||
(str
|
(str
|
||||||
(i18n/label :send-sending-to)
|
(i18n/label :send-sending-to)
|
||||||
" "
|
" "
|
||||||
recipient-name)])
|
recipient-name)])
|
||||||
[react/view
|
[react/view
|
||||||
[react/text {:style (message-styles/command-send-timestamp outgoing)}
|
[react/text {:style (transactions-styles/command-send-timestamp outgoing)}
|
||||||
(str (i18n/label :sent-at) " " timestamp-str)]]
|
(str (i18n/label :sent-at) " " timestamp-str)]]
|
||||||
[send-status tx-hash outgoing]
|
[send-status tx-hash outgoing]
|
||||||
(when network-mismatch?
|
(when network-mismatch?
|
||||||
[react/text send-network])]])))
|
[react/text send-network])]])))
|
||||||
|
|
||||||
|
(def personal-send-request-params
|
||||||
|
[{:id :asset
|
||||||
|
:type :text
|
||||||
|
:placeholder "Currency"
|
||||||
|
;; Suggestion components should be structured in such way that they will just take
|
||||||
|
;; one argument, event-creator fn used to construct event to fire whenever something
|
||||||
|
;; is selected.
|
||||||
|
:suggestions choose-asset}
|
||||||
|
{:id :amount
|
||||||
|
:type :number
|
||||||
|
:placeholder "Amount"}])
|
||||||
|
|
||||||
|
;;TODO(goranjovic): currently we only allow tokens which are enabled in Manage assets here
|
||||||
|
;; because balances are only fetched for them. Revisit this decision with regard to battery/network consequences
|
||||||
|
;; if we were to update all balances.
|
||||||
|
(defn- allowed-assets [{:account/keys [account] :keys [chain]}]
|
||||||
|
(let [chain-keyword (keyword chain)
|
||||||
|
visible-tokens (get-in account [:settings :wallet :visible-tokens chain-keyword])]
|
||||||
|
(into {"ETH" 18}
|
||||||
|
(comp (filter #(and (not (:nft? %))
|
||||||
|
(contains? visible-tokens (:symbol %))))
|
||||||
|
(map (juxt (comp name :symbol) :decimals)))
|
||||||
|
(tokens/tokens-for chain-keyword))))
|
||||||
|
|
||||||
|
(defn- personal-send-request-validation [{:keys [asset amount]} {:keys [db]}]
|
||||||
|
(let [asset-decimals (get (allowed-assets db) asset)]
|
||||||
|
(cond
|
||||||
|
|
||||||
|
(not asset-decimals)
|
||||||
|
{:title "Invalid Asset"
|
||||||
|
:description (str "Unknown token - " asset)}
|
||||||
|
|
||||||
|
(not amount)
|
||||||
|
{:title "Amount"
|
||||||
|
:description "Amount must be specified"}
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [sanitised-str (string/replace amount #"," ".")
|
||||||
|
portions (string/split sanitised-str ".")
|
||||||
|
decimals (get portions 1)
|
||||||
|
amount (js/parseFloat sanitised-str)]
|
||||||
|
(cond
|
||||||
|
|
||||||
|
(or (js/isNaN amount)
|
||||||
|
(> (count portions) 2))
|
||||||
|
{:title "Amount"
|
||||||
|
:description "Amount is not valid number"}
|
||||||
|
|
||||||
|
(and decimals (> decimals asset-decimals))
|
||||||
|
{:title "Amount"
|
||||||
|
:description (str "Max number of decimals is " asset-decimals)})))))
|
||||||
|
|
||||||
|
;; TODO(goranjovic) - update to include tokens in https://github.com/status-im/status-react/issues/3233
|
||||||
|
(defn- transaction-details [contact symbol]
|
||||||
|
(-> contact
|
||||||
|
(select-keys [:name :address :whisper-identity])
|
||||||
|
(assoc :symbol symbol
|
||||||
|
:gas (ethereum/estimate-gas symbol)
|
||||||
|
:from-chat? true)))
|
||||||
|
|
||||||
|
;; `/send` command
|
||||||
|
|
||||||
(deftype PersonalSendCommand []
|
(deftype PersonalSendCommand []
|
||||||
protocol/Command
|
protocol/Command
|
||||||
(id [_]
|
(id [_]
|
||||||
:send)
|
"send")
|
||||||
(scope [_]
|
(scope [_]
|
||||||
#{:personal-chats})
|
#{:personal-chats})
|
||||||
(parameters [_]
|
(parameters [_]
|
||||||
[{:id :asset
|
personal-send-request-params)
|
||||||
:type :text
|
(validate [_ parameters cofx]
|
||||||
:placeholder "Currency"
|
;; Only superficial/formatting validation, "real validation" will be performed
|
||||||
;; Suggestion components should be structured in such way that they will just take
|
;; by the wallet, where we yield control in the next step
|
||||||
;; one argument, event-creator fn used to construct event to fire whenever something
|
(personal-send-request-validation parameters cofx))
|
||||||
;; is selected.
|
(yield-control [_ parameters {:keys [db]}]
|
||||||
:suggestions choose-asset}
|
;; Prefill wallet and navigate there
|
||||||
{:id :amount
|
(let [recipient-contact (get-in db [:contacts/contacts (:current-chat-id db)])
|
||||||
:type :number
|
sender-account (:account/account db)
|
||||||
:placeholder "Amount"}])
|
chain (keyword (:chain db))
|
||||||
(validate [_ _ _]
|
symbol (-> parameters :asset keyword)
|
||||||
;; There is no validation for the `/send` command, as it's fully delegated to the wallet
|
{:keys [decimals]} (tokens/asset-for chain symbol)]
|
||||||
nil)
|
(merge {:db (-> db
|
||||||
(yield-control [_ parameters cofx]
|
(send.events/set-and-validate-amount-db (:amount parameters) symbol decimals)
|
||||||
;; navigate to wallet
|
(choose-recipient.events/fill-request-details
|
||||||
nil)
|
(transaction-details recipient-contact symbol))
|
||||||
(on-send [_ message-id parameters cofx]
|
(update-in [:wallet :send-transaction] dissoc :id :password :wrong-password?)
|
||||||
(when-let [tx-hash (get-in cofx [:db :wallet :send-transaction :tx-hash])]
|
(navigation/navigate-to
|
||||||
{:dispatch [:update-transactions]}))
|
(if (:wallet-set-up-passed? sender-account)
|
||||||
|
:wallet-send-transaction-chat
|
||||||
|
:wallet-onboarding-setup)))}
|
||||||
|
(send.events/update-gas-price db false))))
|
||||||
|
(on-send [_ _ _ _]
|
||||||
|
;; TODO(janherich) - remove this once periodic updates are implemented
|
||||||
|
{:dispatch [:update-transactions]})
|
||||||
(on-receive [_ _ _]
|
(on-receive [_ _ _]
|
||||||
nil)
|
;; TODOD(janherich) - this just copyies the current logic but still seems super weird,
|
||||||
|
;; remove/reconsider once periodic updates are implemented
|
||||||
|
{:dispatch [:update-transactions]
|
||||||
|
:dispatch-later [{:ms constants/command-send-status-update-interval-ms
|
||||||
|
:dispatch [:update-transactions]}]})
|
||||||
(short-preview [_ command-message _]
|
(short-preview [_ command-message _]
|
||||||
(send-short-preview command-message))
|
(personal-send-request-short-preview command-message))
|
||||||
(preview [_ command-message _]
|
(preview [_ command-message _]
|
||||||
(send-preview command-message)))
|
(send-preview command-message)))
|
||||||
|
|
||||||
|
;; `/request` command
|
||||||
|
|
||||||
|
(deftype PersonalRequestCommand []
|
||||||
|
protocol/Command
|
||||||
|
(id [_]
|
||||||
|
"request")
|
||||||
|
(scope [_]
|
||||||
|
#{:personal-chats})
|
||||||
|
(parameters [_]
|
||||||
|
personal-send-request-params)
|
||||||
|
(validate [_ parameters cofx]
|
||||||
|
(personal-send-request-validation parameters cofx))
|
||||||
|
(yield-control [_ _ _])
|
||||||
|
(on-send [_ _ _ _])
|
||||||
|
(on-receive [_ command-message cofx]
|
||||||
|
(let [{:keys [chat-id message-id]} command-message]
|
||||||
|
(request-events/add-request chat-id message-id cofx)))
|
||||||
|
(short-preview [_ command-message _]
|
||||||
|
(personal-send-request-short-preview command-message))
|
||||||
|
(preview [_ command-message _]
|
||||||
|
nil))
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
(ns status-im.chat.commands.impl.transactions.styles
|
(ns status-im.chat.commands.impl.transactions.styles
|
||||||
|
(:require-macros [status-im.utils.styles :refer [defstyle]])
|
||||||
(:require [status-im.ui.components.colors :as colors]))
|
(:require [status-im.ui.components.colors :as colors]))
|
||||||
|
|
||||||
(def asset-container
|
(def asset-container
|
||||||
|
@ -33,3 +34,75 @@
|
||||||
{:height 1
|
{:height 1
|
||||||
:background-color colors/gray-light
|
:background-color colors/gray-light
|
||||||
:margin-left 56})
|
:margin-left 56})
|
||||||
|
|
||||||
|
(def command-send-status-container
|
||||||
|
{:margin-top 6
|
||||||
|
:flex-direction :row})
|
||||||
|
|
||||||
|
(defn command-send-status-icon [outgoing]
|
||||||
|
{:background-color (if outgoing
|
||||||
|
colors/blue-darker
|
||||||
|
colors/blue-transparent)
|
||||||
|
:width 24
|
||||||
|
:height 24
|
||||||
|
:border-radius 16
|
||||||
|
:padding-top 4
|
||||||
|
:padding-left 4})
|
||||||
|
|
||||||
|
(defstyle command-send-status-text
|
||||||
|
{:color colors/blue
|
||||||
|
:android {:margin-top 3}
|
||||||
|
:ios {:margin-top 4}
|
||||||
|
:margin-left 6
|
||||||
|
:font-size 12})
|
||||||
|
|
||||||
|
(def command-send-message-view
|
||||||
|
{:flex-direction :column
|
||||||
|
:align-items :flex-start})
|
||||||
|
|
||||||
|
(def command-send-amount-row
|
||||||
|
{:flex-direction :row
|
||||||
|
:justify-content :space-between})
|
||||||
|
|
||||||
|
(def command-send-amount
|
||||||
|
{:flex-direction :column
|
||||||
|
:align-items :flex-end
|
||||||
|
:max-width 250})
|
||||||
|
|
||||||
|
(defstyle command-send-amount-text
|
||||||
|
{:font-size 22
|
||||||
|
:color colors/blue
|
||||||
|
:ios {:letter-spacing -0.5}})
|
||||||
|
|
||||||
|
(def command-send-currency
|
||||||
|
{:flex-direction :column
|
||||||
|
:align-items :flex-end})
|
||||||
|
|
||||||
|
(defn command-amount-currency-separator [outgoing]
|
||||||
|
{:opacity 0
|
||||||
|
:color (if outgoing colors/hawkes-blue colors/white)})
|
||||||
|
|
||||||
|
(defn command-send-currency-text [outgoing]
|
||||||
|
{:font-size 22
|
||||||
|
:margin-left 4
|
||||||
|
:letter-spacing 1
|
||||||
|
:color (if outgoing colors/wild-blue-yonder colors/blue-transparent-40)})
|
||||||
|
|
||||||
|
(def command-send-fiat-amount
|
||||||
|
{:flex-direction :column
|
||||||
|
:justify-content :flex-end
|
||||||
|
:margin-top 6})
|
||||||
|
|
||||||
|
(def command-send-fiat-amount-text
|
||||||
|
{:font-size 12
|
||||||
|
:color colors/black})
|
||||||
|
|
||||||
|
(def command-send-recipient-text
|
||||||
|
{:color colors/blue
|
||||||
|
:font-size 14
|
||||||
|
:line-height 18})
|
||||||
|
|
||||||
|
(defn command-send-timestamp [outgoing]
|
||||||
|
{:color (if outgoing colors/wild-blue-yonder colors/gray)
|
||||||
|
:margin-top 6
|
||||||
|
:font-size 12})
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
(validate [this parameters cofx]
|
(validate [this parameters cofx]
|
||||||
"Function validating the parameters once command is send. Takes parameters map
|
"Function validating the parameters once command is send. Takes parameters map
|
||||||
and `cofx` map as argument, returns either `nil` meaning that no errors were
|
and `cofx` map as argument, returns either `nil` meaning that no errors were
|
||||||
found and command send workflow can proceed, or sequence of errors to display")
|
found and command send workflow can proceed, or one/more errors to display.
|
||||||
|
Each error is represented by the map containing `:title` and `:description` keys.")
|
||||||
(yield-control [this parameters cofx]
|
(yield-control [this parameters cofx]
|
||||||
"Optional function, which if implemented, can step out of the normal command
|
"Optional function, which if implemented, can step out of the normal command
|
||||||
workflow (`validate-and-send`) and yield control back to application before sending.
|
workflow (`validate-and-send`) and yield control back to application before sending.
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
(ns status-im.chat.commands.styles.validation
|
||||||
|
(:require [status-im.ui.components.styles :as common]))
|
||||||
|
|
||||||
|
(defn root [bottom]
|
||||||
|
{:flex-direction :column
|
||||||
|
:left 0
|
||||||
|
:right 0
|
||||||
|
:bottom bottom
|
||||||
|
:position :absolute})
|
||||||
|
|
||||||
|
(def message-container
|
||||||
|
{:background-color common/color-red
|
||||||
|
:padding 16})
|
||||||
|
|
||||||
|
(def message-title
|
||||||
|
{:color common/color-white
|
||||||
|
:font-size 12})
|
||||||
|
|
||||||
|
(def message-description
|
||||||
|
{:color common/color-white
|
||||||
|
:font-size 12
|
||||||
|
:opacity 0.9})
|
|
@ -0,0 +1,10 @@
|
||||||
|
(ns status-im.chat.commands.validation
|
||||||
|
(:require [status-im.ui.components.react :as react]
|
||||||
|
[status-im.chat.commands.styles.validation :as styles]))
|
||||||
|
|
||||||
|
(defn validation-message [{:keys [title description]}]
|
||||||
|
[react/view styles/message-container
|
||||||
|
[react/text {:style styles/message-title}
|
||||||
|
title]
|
||||||
|
[react/text {:style styles/message-description}
|
||||||
|
description]])
|
|
@ -0,0 +1,38 @@
|
||||||
|
(ns status-im.test.chat.commands.impl.transactions
|
||||||
|
(:require [cljs.test :refer-macros [deftest is testing]]
|
||||||
|
[status-im.chat.commands.impl.transactions :as transactions]
|
||||||
|
[status-im.chat.commands.protocol :as protocol]))
|
||||||
|
|
||||||
|
(def personal-send-command (transactions/PersonalSendCommand.))
|
||||||
|
|
||||||
|
(def cofx {:db {:account/account {:settings {:wallet {:visible-tokens {:mainnet #{:SNT}}}}
|
||||||
|
:wallet-set-up-passed? true}
|
||||||
|
:chain "mainnet"
|
||||||
|
:current-chat-id "recipient"
|
||||||
|
:contacts/contacts {"recipient" {:name "Recipient"
|
||||||
|
:address "0xAA"
|
||||||
|
:whisper-identity "0xBB"}}}})
|
||||||
|
|
||||||
|
(deftest personal-send-command-test
|
||||||
|
(testing "That correct parameters are defined"
|
||||||
|
(is (= (into #{} (map :id) (protocol/parameters personal-send-command))
|
||||||
|
#{:asset :amount})))
|
||||||
|
(testing "Parameters validation"
|
||||||
|
(is (= (protocol/validate personal-send-command {:asset "TST"} cofx)
|
||||||
|
{:title "Invalid Asset"
|
||||||
|
:description "Unknown token - TST"}))
|
||||||
|
(is (= (protocol/validate personal-send-command {:asset "SNT"} cofx)
|
||||||
|
{:title "Amount"
|
||||||
|
:description "Amount must be specified"}))
|
||||||
|
(is (= (protocol/validate personal-send-command {:asset "SNT" :amount "a"} cofx)
|
||||||
|
{:title "Amount"
|
||||||
|
:description "Amount is not valid number"}))
|
||||||
|
(is (= (protocol/validate personal-send-command {:asset "ETH" :amount "0.54354353454353453453454353453445345545"} cofx)
|
||||||
|
{:title "Amount"
|
||||||
|
:description "Max number of decimals is 18"}))
|
||||||
|
(is (= (protocol/validate personal-send-command {:asset "ETH" :amount "0.01"} cofx)
|
||||||
|
nil)))
|
||||||
|
(testing "Yielding control prefills wallet"
|
||||||
|
(let [fx (protocol/yield-control personal-send-command {:asset "ETH" :amount "0.01"} cofx)]
|
||||||
|
(is (= (get-in fx [:db :wallet :send-transaction :amount-text]) "0.01"))
|
||||||
|
(is (= (get-in fx [:db :wallet :send-transaction :symbol]) :ETH)))))
|
|
@ -27,6 +27,7 @@
|
||||||
[status-im.test.chat.views.message]
|
[status-im.test.chat.views.message]
|
||||||
[status-im.test.chat.views.photos]
|
[status-im.test.chat.views.photos]
|
||||||
[status-im.test.chat.commands.core]
|
[status-im.test.chat.commands.core]
|
||||||
|
[status-im.test.chat.commands.impl.transactions]
|
||||||
[status-im.test.i18n]
|
[status-im.test.i18n]
|
||||||
[status-im.test.protocol.web3.inbox]
|
[status-im.test.protocol.web3.inbox]
|
||||||
[status-im.test.utils.utils]
|
[status-im.test.utils.utils]
|
||||||
|
@ -82,6 +83,7 @@
|
||||||
'status-im.test.chat.views.message
|
'status-im.test.chat.views.message
|
||||||
'status-im.test.chat.views.photos
|
'status-im.test.chat.views.photos
|
||||||
'status-im.test.chat.commands.core
|
'status-im.test.chat.commands.core
|
||||||
|
'status-im.test.chat.commands.impl.transactions
|
||||||
'status-im.test.i18n
|
'status-im.test.i18n
|
||||||
'status-im.test.transport.core
|
'status-im.test.transport.core
|
||||||
'status-im.test.transport.inbox
|
'status-im.test.transport.inbox
|
||||||
|
|
Loading…
Reference in New Issue