From d0affa54aef97476f2c185d0f7f6a8f6815cbe8f Mon Sep 17 00:00:00 2001 From: Jack Leslie Date: Sun, 2 Jun 2019 23:34:17 +0100 Subject: [PATCH 1/4] Moved languages to manager --- package.json | 2 +- src/components/CopyToClipboard/index.js | 2 +- src/components/Footer.js | 2 +- src/components/GasButton/index.js | 2 +- src/components/Input.js | 2 +- src/components/SubscribeForm.js | 3 +- src/components/TabMenu.js | 2 +- src/components/TransactionStatus.js | 2 +- src/components/UniqueToken.js | 2 +- src/handlers/trezor-eth.js | 2 +- src/index.js | 3 +- src/languages/_brazilian.json | 196 +++++++++++++ src/languages/_czech.json | 201 +++++++++++++ src/languages/_english.json | 294 ++++++++++++++++++++ src/languages/_french.json | 258 +++++++++++++++++ src/languages/_german.json | 166 +++++++++++ src/languages/_greek.json | 196 +++++++++++++ src/languages/_italian.json | 166 +++++++++++ src/languages/_polish.json | 196 +++++++++++++ src/languages/_portuguese.json | 196 +++++++++++++ src/languages/_russian.json | 195 +++++++++++++ src/languages/_spanish.json | 179 ++++++++++++ src/languages/index.js | 38 +++ src/layouts/base.js | 2 +- src/modals/ApproveTransactionModal/index.js | 3 +- src/modals/DonateModal/index.js | 2 +- src/modals/ExchangeModal/index.js | 2 +- src/modals/ReceiveModal/index.js | 3 +- src/modals/SendModal/index.js | 2 +- src/modals/SuccessModal/index.js | 2 +- src/modals/WalletConnectModal.js | 2 +- src/pages/Ledger.js | 2 +- src/pages/Metamask.js | 2 +- src/pages/NotFound.js | 2 +- src/pages/Trezor.js | 2 +- src/pages/Wallet.js | 2 +- src/pages/index.js | 3 +- src/reducers/_ledger.js | 2 +- src/reducers/_trezor.js | 2 +- src/reducers/_warning.js | 2 +- src/views/Account/AccountBalances.js | 2 +- src/views/Account/AccountTransactions.js | 2 +- src/views/Account/AccountUniqueTokens.js | 2 +- src/views/Account/index.js | 3 +- yarn.lock | 23 +- 45 files changed, 2334 insertions(+), 40 deletions(-) create mode 100644 src/languages/_brazilian.json create mode 100644 src/languages/_czech.json create mode 100644 src/languages/_english.json create mode 100644 src/languages/_french.json create mode 100644 src/languages/_german.json create mode 100644 src/languages/_greek.json create mode 100644 src/languages/_italian.json create mode 100644 src/languages/_polish.json create mode 100644 src/languages/_portuguese.json create mode 100644 src/languages/_russian.json create mode 100644 src/languages/_spanish.json create mode 100644 src/languages/index.js diff --git a/package.json b/package.json index 7b4ada1a..a68449ec 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "@walletconnect/browser": "^1.0.0-beta.18", "@walletconnect/qrcode-modal": "^1.0.0-beta.18", "axios": "^0.18.0", - "balance-common": "^0.6.1", + "balance-common": "../balance-common/", "bignumber.js": "^7.0.1", "bowser": "^2.0.0-alpha.4", "ethereumjs-tx": "^1.3.4", diff --git a/src/components/CopyToClipboard/index.js b/src/components/CopyToClipboard/index.js index 89089624..6e8869fa 100644 --- a/src/components/CopyToClipboard/index.js +++ b/src/components/CopyToClipboard/index.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { lang } from 'balance-common'; +import lang from '../../languages'; import clipboardIcon from '../../assets/clipboard.png'; import { toChecksumAddress } from 'balance-common'; import { notificationShow } from '../../reducers/_notification'; diff --git a/src/components/Footer.js b/src/components/Footer.js index 7c6ed458..57a219cb 100644 --- a/src/components/Footer.js +++ b/src/components/Footer.js @@ -1,6 +1,6 @@ import React from 'react'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../languages'; import OpenSeaLogo from '../assets/opensea-logo.svg'; import { fonts, responsive } from '../styles'; diff --git a/src/components/GasButton/index.js b/src/components/GasButton/index.js index c495336f..c8e0c7b3 100644 --- a/src/components/GasButton/index.js +++ b/src/components/GasButton/index.js @@ -1,6 +1,6 @@ import React from 'react'; -import { lang } from 'balance-common'; +import lang from '../../languages'; import { StyledGasButton } from './styles'; diff --git a/src/components/Input.js b/src/components/Input.js index e51ff50f..b85855df 100644 --- a/src/components/Input.js +++ b/src/components/Input.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled, { keyframes } from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../languages'; import { colors, fonts, shadows, responsive } from '../styles'; const shimmer = keyframes` diff --git a/src/components/SubscribeForm.js b/src/components/SubscribeForm.js index 8caa1882..8d186fb6 100644 --- a/src/components/SubscribeForm.js +++ b/src/components/SubscribeForm.js @@ -3,7 +3,8 @@ import styled from 'styled-components'; import PropTypes from 'prop-types'; import jsonp from 'jsonp'; import Button from './Button'; -import { isValidEmail, lang } from 'balance-common'; +import { isValidEmail } from 'balance-common'; +import lang from '../languages'; import { fonts, colors, transitions } from '../styles'; const SForm = styled.form` diff --git a/src/components/TabMenu.js b/src/components/TabMenu.js index 12b9a7d1..751dc2ff 100644 --- a/src/components/TabMenu.js +++ b/src/components/TabMenu.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import ReactDOM from 'react-dom'; import PropTypes from 'prop-types'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../languages'; import i18next from 'i18next'; import Link from './Link'; import Button from './Button'; diff --git a/src/components/TransactionStatus.js b/src/components/TransactionStatus.js index 435b92c5..f7397ca9 100644 --- a/src/components/TransactionStatus.js +++ b/src/components/TransactionStatus.js @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import Spinner from './Spinner'; -import { lang } from 'balance-common'; +import lang from '../languages'; import circle from '../assets/circle.svg'; import txSentIcon from '../assets/arrow-sent.svg'; import txReceivedIcon from '../assets/arrow-received.svg'; diff --git a/src/components/UniqueToken.js b/src/components/UniqueToken.js index c9795a85..5e05b11f 100644 --- a/src/components/UniqueToken.js +++ b/src/components/UniqueToken.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../languages'; import { colors, fonts, shadows, transitions } from '../styles'; const StyledViewLink = styled.a` diff --git a/src/handlers/trezor-eth.js b/src/handlers/trezor-eth.js index 413df2a9..4ac2af2d 100644 --- a/src/handlers/trezor-eth.js +++ b/src/handlers/trezor-eth.js @@ -1,6 +1,6 @@ import EthereumTx from 'ethereumjs-tx'; import ethereumNetworks from '../references/ethereum-networks.json'; -import { lang } from 'balance-common'; +import lang from '../languages'; const HDKey = require('ethereumjs-wallet/hdkey'); diff --git a/src/index.js b/src/index.js index c65e2803..6f593f72 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,8 @@ import { globalStyles } from './styles'; import { bootIntercom } from './helpers/bootIntercom'; import Root from './Root'; import Storage from '@devshack/react-native-storage'; -import { commonStorage, lang, resources } from 'balance-common'; +import { commonStorage } from 'balance-common'; +import lang, { resources } from './languages'; const storage = new Storage({ size: 1000, diff --git a/src/languages/_brazilian.json b/src/languages/_brazilian.json new file mode 100644 index 00000000..d637a38c --- /dev/null +++ b/src/languages/_brazilian.json @@ -0,0 +1,196 @@ +{ + "translation": { + "account": { + "hide": "Ocultar", + "label_24h": "24h", + "label_asset": "Ativos", + "label_price": "Preço", + "label_quantity": "Quantidade", + "label_status": "Status", + "label_total": "Total", + "low_market_value": "baixo valor de mercado", + "no_market_value": "sem valor de mercado", + "show": "Expandir", + "show_all": "Mostrar tudo", + "show_less": "Mostrar menos", + "tab_balances": "Saldos", + "tab_balances_tooltip": "Ethereum e token saldos", + "tab_interactions": "Interações", + "tab_interactions_tooltip": "Interações de Smart Contract", + "tab_transactions": "Transações", + "tab_transactions_tooltip": "Transações e transferências de tokens", + "tab_uniquetokens": "Tokens exclusivos", + "tab_uniquetokens_tooltip": "Tokens exclusivos", + "token": "token", + "tokens": "tokens", + "tx_failed": "Falhou", + "tx_fee": "Taxa", + "tx_from": "De", + "tx_hash": "Hash de transação", + "tx_pending": "Pendente", + "tx_received": "Recebido", + "tx_self": "Eu", + "tx_sent": "Enviado", + "tx_timestamp": "Data/hora", + "tx_to": "Para", + "unknown_token": "Token desconhecido" + }, + "button": { + "cancel": "Cancelar", + "close": "Fechar", + "confirm": "Confirme", + "connect_walletconnect": "Use o WalletConnect", + "donate": "Doe ETH", + "exchange": "Troca", + "exchange_again": "Troque novamente", + "go_back": "Volte", + "learn_more": "Saiba mais", + "notify_me": "Receba notificação", + "receive": "Receber", + "send": "Enviar", + "send_another": "Enviar outro", + "try_again": "Tente novamente", + "view": "Visualizar" + }, + "homepage": { + "coming_soon": "Em breve.", + "connect_ledger": { + "button": "Conecte-se ao Ledger", + "description": "Conecte-se e assine com seu ", + "link_text": "Ledger wallet", + "link_title": "Compre uma carteira de Ledger hardware wallet" + }, + "connect_metamask": { + "button": "Conecte-se ao MetaMask", + "description": "Conecte-se ao MetaMask Chrome extensão." + }, + "connect_trezor": { + "button": "Conecte-se a Trezor", + "description": "Conecte-se e assine com seu ", + "link_text": "Trezor hardware wallet", + "link_title": "Compre uma carteira de Trezor hardware wallet" + }, + "connect_walletconnect": "Ligue e assine com a sua carteira móvel compatível com o WalletConnect." + }, + "input": { + "asset_amount": "Quantidade", + "donation_address": "Balance Manager endereço", + "email": "Email", + "email_placeholder": "seu@email.com", + "input_placeholder": "Digite aqui", + "input_text": "Entrada", + "password": "Senha", + "password_placeholder": "••••••••••", + "private_key": "Chave privada", + "recipient_address": "Endereço do destinatário" + }, + "message": { + "click_to_copy_to_clipboard": "Clique para copiar para a área de transferência", + "coming_soon": "Em breve...", + "exchange_not_available": "O Exchange não está disponível na sua área", + "failed_ledger_connection": "Falha ao conectar-se ao Ledger, verifique seu dispositivo", + "failed_request": "Falha no pedido, por favor atualize", + "failed_trezor_connection": "Falha ao conectar-se ao Trezor, verifique seu dispositivo", + "failed_trezor_popup_blocked": "Por favor, permita que os popups do Balance usem o seu Trezor", + "learn_more": "Saiba mais", + "no_interactions": "Nenhuma interação encontrada para esta conta", + "no_transactions": "Nenhuma transação encontrada para esta conta", + "no_unique_tokens": "Nenhum token exclusivo encontrado para esta conta", + "opensea_footer": "é um mercado para tokens exclusivos (ou 'não fungíveis'). As pessoas negociam no espaço de mercado e isso lhes dá valor. Você pode penhorar seus tokens para conseguir dinheiro. Tudo isso é executado no Ethereum.", + "opensea_header": "Como isso funciona?", + "page_not_found": "404 Página Não Encontrada", + "please_connect_ledger": "Por favor, conecte e desbloqueie o Ledger e selecione Ethereum", + "please_connect_trezor": "Por favor, conecte seu Trezor e siga as instruções", + "power_by": "Distribuído por", + "web3_not_available": "Por favor, instale a extensão MetaMask Chrome", + "web3_not_unlocked": "Por favor, desbloqueie sua carteira MetaMask", + "web3_unknown_network": "Rede desconhecida, por favor mude para outra" + }, + "modal": { + "approve_tx": "Aprovar transação no {{walletType}}", + "confirm_tx": "Confirme a transação de {{walletName}}", + "default_wallet": "Carteira", + "deposit_dropdown_label": "Trocar meu", + "deposit_input_label": "Pagamento", + "donate_title": "Enviar de {{walletName}} para o Gestor de saldo", + "exchange_fee": "Taxa de troca", + "exchange_max": "Troca máxima", + "exchange_title": "Trocar de {{walletName}}", + "gas_average": "Médio", + "gas_fast": "Rápido", + "gas_fee": "Taxa", + "gas_slow": "Lento", + "helper_balance": "Balanço", + "helper_max": "Max", + "helper_min": "Min", + "helper_price": "Preço", + "helper_rate": "Taxa", + "helper_value": "Valor", + "invalid_address": "Endereço inválido", + "new": "Novo", + "previous_short": "Prev.", + "receive_title": "Receber para {{walletName}}", + "send_max": "Enviar max", + "send_title": "Enviar de {{walletName}}", + "tx_confirm_amount": "Montante", + "tx_confirm_fee": "Taxa de transação", + "tx_confirm_recipient": "Destinatário", + "tx_confirm_sender": "Remetente", + "tx_fee": "Taxa de transação", + "tx_hash": "Hash de transação", + "tx_verify": "Verifique sua transação aqui", + "withdrawal_dropdown_label": "Para", + "withdrawal_input_label": "Obter" + }, + "notification": { + "error": { + "failed_get_account_tx": "Falha ao obter transações da conta", + "failed_get_gas_prices": "Não foi possível obter os preços da Ethereum Gas", + "failed_get_tx_fee": "Não foi possível estimar a taxa de transação", + "failed_scanning_qr_code": "Falha ao digitalizar o código QR, por favor, tente novamente", + "generic_error": "Algo deu errado. Por favor tente outra vez", + "insufficient_balance": "Saldo insuficiente nesta conta", + "insufficient_for_fees": "Saldo insuficiente para cobrir as taxas de transação", + "invalid_address": "Endereço inválido, por favor verifique novamente", + "invalid_address_scanned": "Endereço inválido digitalizado, tente novamente", + "invalid_private_key_scanned": "Chave privada inválida, tente novamente", + "no_accounts_found": "Nenhuma Conta Ethereum Encontrada" + }, + "info": { + "address_copied_to_clipboard": "Endereço copiado para a área de transferência" + } + }, + "subscribe_form": { + "email_already_subscribed": "Desculpe, você já se inscreveu com este e-mail", + "generic_error": "Oops, algo deu errado", + "sending": "Enviando ...", + "successful": "Verifique seu e-mail", + "too_many_signup_request": "Muitas solicitações de inscrição, tente novamente mais tarde" + }, + "time": { + "day": "dia", + "days": "dias", + "hour": "hora", + "hours": "horas", + "hr": "hr", + "hrs": "hrs", + "milisecond": "milisegundo", + "miliseconds": "milissegundos", + "min": "min", + "mins": "min", + "minute": "minuto", + "minutes": "minutos", + "ms": "ms", + "now": "Agora", + "s": "s", + "sec": "sec", + "second": "segundo", + "seconds": "segundos", + "secs": "sec" + }, + "warning": { + "user_is_offline": "Conexão offline, por favor, verifique sua conexão com a internet", + "user_is_online": "Conectado! Você está de volta online" + } + } +} diff --git a/src/languages/_czech.json b/src/languages/_czech.json new file mode 100644 index 00000000..bbd2ab0c --- /dev/null +++ b/src/languages/_czech.json @@ -0,0 +1,201 @@ +{ + "translation": { + "homepage": { + "connect_metamask": { + "description": "Připojit se k MetaMask rozšíření.", + "button": "Připojit se k MetaMask" + }, + "connect_ledger": { + "description": "Připojit se a podepsat pomocí ", + "link_text": "Ledger HW peněženky", + "link_title": "Koupit Ledger HW peněženku", + "button": "Připojit se k Ledger" + }, + "connect_trezor": { + "description": "Připojit se a podepsat pomocí ", + "link_text": "Trezor HW peněženky", + "link_title": "Koupit Trezor HW peněženku", + "button": "Připojit se k Trezoru" + }, + "connect_walletconnect": { + "description": "Připojit se a podepsat pomocí WalletConnect-enabled mobilní peněženky.", + "link_text": "Trezor hardware wallet", + "link_title": "Buy a Trezor hardware wallet", + "button": "Connect with WalletConnect" + }, + "coming_soon": "Již brzy." + }, + "account": { + "tab_balances": "Zůstatky", + "tab_transactions": "Transakce", + "tab_interactions": "Interakce", + "tab_uniquetokens": "Unikátní tokeny", + "tab_balances_tooltip": "Zůstatky Etheru a Tokenů", + "tab_transactions_tooltip": "Transakce a transakce tokenů", + "tab_interactions_tooltip": "Smart Contract interakce", + "tab_uniquetokens_tooltip": "Unikátní tokeny", + "label_asset": "Aktivum", + "label_quantity": "Množství", + "label_price": "Cena", + "label_24h": "24h", + "label_status": "Status", + "label_total": "Celkem", + "show": "Zobraz", + "hide": "Schovej", + "token": "token", + "tokens": "tokeny", + "no_market_value": "bez tržní hodnoty", + "low_market_value": "s nízkou tržní hodnotou", + "tx_hash": "Hash transakce", + "tx_timestamp": "Datum a čas", + "tx_fee": "Poplatek", + "tx_to": "Komu", + "tx_from": "Od", + "tx_self": "Sobě", + "tx_sent": "Posláno", + "tx_received": "Obdrženo", + "tx_failed": "Selhalo", + "tx_pending": "Probíhá", + "show_all": "Zobrazit všechny", + "show_less": "Zobraz méně", + "unknown_token": "Neznámý Token" + }, + "button": { + "connect_walletconnect": "Použij WalletConnect", + "exchange": "Směnit", + "send": "Poslat", + "donate": "Daruj ETH", + "receive": "Příjmout", + "close": "Zavřít", + "confirm": "Potvrdit", + "cancel": "Zrušit", + "go_back": "Jít zpět", + "send_another": "Poslat další", + "exchange_again": "Směn znovu", + "notify_me": "Dostávat oznámení", + "try_again": "Zkusit znovu", + "learn_more": "Zjistit více", + "view": "Zobrazit" + }, + "input": { + "recipient_address": "Adresa příjemce", + "private_key": "Soukromý klíč", + "donation_address": "Správa zůstatků", + "asset_amount": "množství", + "email": "Email", + "password": "Heslo", + "input_text": "Vstup", + "email_placeholder": "your@email.com", + "password_placeholder": "••••••••••", + "input_placeholder": "Vepište zde" + }, + "modal": { + "receive_title": "Příjmout pomocí {{walletName}}", + "exchange_title": "Směnit pomocí {{walletName}}", + "donate_title": "Odeslat z {{walletName}} do správy prostředků", + "send_title": "Odeslat z {{walletName}}", + "approve_tx": "Schválit transakci na {{walletType}}", + "confirm_tx": "Potvrdit transakci od {{walletName}}", + "invalid_address": "Chybná adresa", + "send_max": "Poslat maximum", + "exchange_max": "Směnit maximum", + "default_wallet": " peněženky", + "gas_slow": "Pomalu", + "gas_average": "Průměrně", + "gas_fast": "Rychle", + "gas_fee": "Poplatek", + "tx_confirm_sender": "Odesílatel", + "tx_confirm_recipient": "Příjemce", + "tx_confirm_amount": "Množství", + "tx_confirm_fee": "Poplatek za transakci", + "tx_hash": "Hash transakce", + "tx_verify": "Ověřte si transakci zde", + "deposit_dropdown_label": "Směn můj", + "withdrawal_dropdown_label": "Za", + "deposit_input_label": "Zaplatit", + "withdrawal_input_label": "Získat", + "tx_fee": "Poplatek za transakci", + "exchange_fee": "Poplatek za směnu", + "previous_short": "Před.", + "new": "Nový", + "helper_balance": "Zůstatek", + "helper_value": "Hodnota", + "helper_rate": "Sazba", + "helper_price": "Cena", + "helper_min": "Min", + "helper_max": "Max" + }, + "message": { + "web3_not_available": "Nainstalujte si prosím MetaMask rozšíření", + "web3_not_unlocked": "Odblokujte prosím MetaMask", + "web3_unknown_network": "Neznámá síť, prosím přepněte se do jinačí sítě", + "page_not_found": "404 Stránka nenalezena", + "coming_soon": "Již brzy...", + "click_to_copy_to_clipboard": "Klikněte pro zkopírování do schránky", + "failed_request": "Požadavek selhal, prosím opakujte", + "failed_ledger_connection": "Nepodařilo se připojit k Ledger, prosím zkontrolujte Vaše zařízení", + "failed_trezor_connection": "Nepodařilo se připojit k Trezoru, prosím zkontrolujte Vaše zařízení", + "failed_trezor_popup_blocked": "Prosím povolte vyskakovací okna v Zůstatcích pro používání Trezoru", + "no_transactions": "Nebyly nalezeny žádné transakce pro Váš účet", + "no_unique_tokens": "Nebyly nalezeny žádné unikátní tokeny pro Váš účet", + "no_interactions": "Nebyly nalezeny žádné interakce pro Váš účet", + "please_connect_ledger": "Prosím připojte a odemkněte Ledger a následně vyberte možnost Ethereum", + "please_connect_trezor": "Prosím připojte Trezor a následujte instrukce", + "opensea_footer": " je market pro unikátní (též 'nezaměnitelné') tokeny. Lidé obchodují na trhu a to jim získává hodnotu. Můžete zastavit Vaše tokeny a tím získat peníze. A vše tohleto běží na Ethereum. ", + "opensea_header": "A jak tohle funguje pod kapotou?", + "power_by": "Poháněno", + "exchange_not_available": "Směnárna neni povolená ve Vaší lokalitě", + "learn_more": "Zjistit více" + }, + "notification": { + "error": { + "no_accounts_found": "Nebyly nalezeny žádné Ethereum účty", + "invalid_address": "Adresa je chybná, prosím zkontrolujte jí", + "insufficient_balance": "Nedostatečný zůstatek na tomto účtě", + "insufficient_for_fees": "Nedostatečný zůstatek pro pokrytí poplatků", + "invalid_address_scanned": "Naskenovaná adresa je chybná, prosím zkuste to znovu", + "invalid_private_key_scanned": "Naskenovaný privátní klíč je chybný. prosím zkuste to znovu", + "failed_scanning_qr_code": "Nepodařilo se naskenovat QR kód, prosím zkuste to pozdějí", + "failed_get_gas_prices": "Nepodařilo se získat cenu za Ethereum plyn", + "failed_get_tx_fee": "Chyba v počítání poplatku za plyn", + "failed_get_account_tx": "Chyba v získávání transakcí k účtu", + "generic_error": "Něco se stalo, zkuste to prosím pozdějí" + }, + "info": { + "address_copied_to_clipboard": "Adresa zkopírována do schránky" + } + }, + "warning": { + "user_is_offline": "Jste offline, prosíme zkontrolujte internetové připojení", + "user_is_online": "Připojeno! Jste zpátky online" + }, + "subscribe_form": { + "email_already_subscribed": "S tímto emailem jste již zaregistrování", + "too_many_signup_request": "Mnoho chybných pokusů o přihlášení, zkuste to prosím později", + "successful": "Zkontrolujte si svůj email", + "sending": "Odesílám...", + "generic_error": "Hups, něco se pokazilo" + }, + "time": { + "ms": "ms", + "milisecond": "milisekunda", + "miliseconds": "milisekundy", + "s": "s", + "sec": "sek", + "secs": "sek", + "second": "sekunda", + "seconds": "sekundy", + "min": "min", + "mins": "min.", + "minute": "minuta", + "minutes": "minuty", + "hr": "hod", + "hrs": "hod", + "hour": "hodina", + "hours": "hodin", + "day": "den", + "days": "dnů", + "now": "nyní" + } + } +} diff --git a/src/languages/_english.json b/src/languages/_english.json new file mode 100644 index 00000000..5b2c0f12 --- /dev/null +++ b/src/languages/_english.json @@ -0,0 +1,294 @@ +{ + "translation": { + "homepage": { + "connect_metamask": { + "description": "Connect to the ", + "link_text": "MetaMask browser wallet", + "link_title": "MetaMask browser wallet", + "button": "Connect to MetaMask" + }, + "connect_ledger": { + "description": "Connect and sign with your ", + "link_text": "Ledger hardware wallet", + "link_title": "Buy a Ledger hardware wallet", + "button": "Connect to Ledger" + }, + "connect_trezor": { + "description": "Connect and sign with your ", + "link_text": "Trezor hardware wallet", + "link_title": "Buy a Trezor hardware wallet", + "button": "Connect to Trezor" + }, + "connect_trustwallet": { + "description_part_one": "Use the ", + "description_part_two": " Ethereum ", + "description_part_three": " app to connect.", + "link_text_wallet": "Trust Wallet", + "link_title_wallet": "Discover Trust Wallet", + "link_text_browser": "dapp browser", + "link_title_browser": "Discover Trust DApp Browser", + "button": "Connect to Trust Wallet" + }, + "connect_walletconnect": { + "description": "Scan a QR code to link your mobile wallet ", + "description_mobile": "Connect to any wallet that supports ", + "link_text": "using WalletConnect", + "link_text_mobile": "WalletConnect", + "link_title": "Use WalletConnect", + "link_title_mobile": "Connect with WalletConnect", + "button": "Use WalletConnect", + "button_mobile": "Connect with WalletConnect" + }, + "reassurance": { + "work_title": "How does Manager work?", + "work": "This is a web-based tool to help you manage the funds in your wallet. It does this by connecting to your wallet through its Application Programming Interface (API). Here are some important points about how we designed Balance Manager:", + "access_link": "No access to your funds", + "tracking_link": "We do not track you", + "security_title": "How secure is Manager?", + "security": "We work incredibly hard to make sure your funds are safe. This tool never touches your private keys, so that greatly reduces the surface area for attack. If you are familiar with programming, you can check out our code on GitHub.", + "source": "View our source code", + "assessment": "Results of our security assessment", + "text_mobile": "You need to use an Ethereum Wallet to access Balance Manager to view, send and exchange your Ether and Ethereum-based tokens." + }, + "back": "Back to balance.io", + "coming_soon": "Coming soon." + }, + "account": { + "tab_balances": "Balances", + "tab_collectibles": "Collectibles", + "tab_transactions": "Transactions", + "tab_interactions": "Interactions", + "tab_uniquetokens": "Unique Tokens", + "tab_balances_tooltip": "Ethereum and Token Balances", + "tab_transactions_tooltip": "Transactions and Token Transfers", + "tab_interactions_tooltip": "Smart Contract Interactions", + "tab_uniquetokens_tooltip": "Unique Tokens", + "label_asset": "Asset", + "label_quantity": "Quantity", + "label_price": "Price", + "label_24h": "24h", + "label_status": "Status", + "label_total": "Total", + "show": "Show", + "hide": "Hide", + "token": "token", + "tokens": "tokens", + "no_market_value": "with no market value", + "low_market_value": "with low market value", + "tx_hash": "Transaction Hash", + "tx_timestamp": "Timestamp", + "tx_fee": "Fee", + "tx_to": "To", + "tx_from": "From", + "tx_self": "Self", + "tx_sent": "Sent", + "tx_received": "Received", + "tx_failed": "Failed", + "tx_pending": "Pending", + "show_all": "Show all", + "show_less": "Show less", + "unknown_token": "Unknown Token" + }, + "button": { + "connect_walletconnect": "Use WalletConnect", + "exchange": "Exchange", + "send": "Send", + "disconnect_account": "Log Out", + "donate": "Donate ETH", + "receive": "Receive", + "close": "Close", + "confirm": "Confirm", + "cancel": "Cancel", + "go_back": "Go Back", + "send_another": "Send Another", + "exchange_again": "Exchange Again", + "notify_me": "Get notified", + "try_again": "Try again", + "learn_more": "Learn more", + "view": "View" + }, + "input": { + "recipient_address": "Recipient Address", + "private_key": "Private Key", + "donation_address": "Balance Manager Address", + "asset_amount": "Amount", + "email": "Email", + "password": "Password", + "input_text": "Input", + "email_placeholder": "your@email.com", + "password_placeholder": "••••••••••", + "input_placeholder": "Type here" + }, + "modal": { + "receive_title": "Receive to {{walletName}}", + "exchange_title": "Exchange from {{walletName}}", + "donate_title": "Send from {{walletName}} to Balance Manager", + "send_title": "Send from {{walletName}}", + "approve_tx": "Approve transaction on {{walletType}}", + "confirm_tx": "Confirm transaction from {{walletName}}", + "invalid_address": "Invalid Address", + "send_max": "Send max", + "exchange_max": "Exchange max", + "default_wallet": " Wallet", + "gas_slow": "Slow", + "gas_average": "Average", + "gas_fast": "Fast", + "gas_fee": "Fee", + "tx_confirm_sender": "Sender", + "tx_confirm_recipient": "Recipient", + "tx_confirm_amount": "Amount", + "tx_confirm_fee": "Transaction Fee", + "tx_hash": "Transaction Hash", + "tx_verify": "Verify your transaction here", + "deposit_dropdown_label": "Exchange my", + "withdrawal_dropdown_label": "For", + "deposit_input_label": "Pay", + "withdrawal_input_label": "Get", + "tx_fee": "Transaction Fee", + "exchange_fee": "Exchange Fee", + "previous_short": "Prev.", + "new": "New", + "helper_balance": "Balance", + "helper_value": "Value", + "helper_rate": "Rate", + "helper_price": "Price", + "helper_min": "Min", + "helper_max": "Max" + }, + "message": { + "web3_not_available": "Please install the MetaMask Chrome extension", + "web3_not_unlocked": "Please unlock your MetaMask wallet", + "walletconnect_not_unlocked": "Please connect using WalletConnect", + "web3_unknown_network": "Unknown network, please switch to another one", + "page_not_found": "404 Page Not Found", + "coming_soon": "Coming soon...", + "click_to_copy_to_clipboard": "Click to copy to clipboard", + "failed_request": "Failed request, please refresh", + "failed_ledger_connection": "Failed to connect to Ledger, please check your device", + "failed_trezor_connection": "Failed to connect to Trezor, please check your device", + "failed_trezor_popup_blocked": "Please allow popups on Balance to use your Trezor", + "no_transactions": "No transactions found for this account", + "no_unique_tokens": "No unique tokens found for this account", + "no_interactions": "No interactions found for this account", + "please_connect_ledger": "Please connect and unlock Ledger then select Ethereum", + "please_connect_trezor": "Please connect your Trezor and follow the instructions", + "opensea_footer": " is a market place for unique (or 'non-fungible') tokens. People trade on the marketspace and that gives them value. You can pawn your tokens to get money. All of this runs on Ethereum. ", + "opensea_header": "How does this work under the hood?", + "power_by": "Powered by", + "exchange_not_available": "We are no longer supporting Shapeshift due to their new KYC requirements. We are working on support for different exchange providers.", + "learn_more": "Learn more" + }, + "notification": { + "error": { + "no_accounts_found": "No Ethereum Accounts Found", + "invalid_address": "Address is invalid, please check again", + "insufficient_balance": "Insufficient balance in this account", + "insufficient_for_fees": "Insufficient balance to cover transaction fees", + "invalid_address_scanned": "Invalid Address Scanned, please try again", + "invalid_private_key_scanned": "Invalid Private Key Scanned, please try again", + "failed_scanning_qr_code": "Failed to scan QR code, please try again", + "failed_get_gas_prices": "Failed to get Ethereum Gas prices", + "failed_get_tx_fee": "Failed to estimate Transaction fee", + "failed_get_account_tx": "Failed to get Account transactions", + "generic_error": "Something went wrong, please try again" + }, + "info": { + "address_copied_to_clipboard": "Address copied to clipboard" + } + }, + "warning": { + "user_is_offline": "Connection offline, please check your internet connection", + "user_is_online": "Connected! You're back online" + }, + "subscribe_form": { + "email_already_subscribed": "Sorry, you've already signed up with this email", + "too_many_signup_request": "Too many signup requests, please try again later", + "successful": "Check your email", + "sending": "Sending...", + "generic_error": "Oops, something went wrong" + }, + "time": { + "ms": "ms", + "milisecond": "milisecond", + "miliseconds": "miliseconds", + "s": "s", + "sec": "sec", + "secs": "secs", + "second": "second", + "seconds": "seconds", + "min": "min", + "mins": "mins", + "minute": "minute", + "minutes": "minutes", + "hr": "hr", + "hrs": "hrs", + "hour": "hour", + "hours": "hours", + "day": "day", + "days": "days", + "now": "Now" + }, + "wallet": { + "action": { + "cancel": "Cancel", + "paste": "Paste", + "reject": "Reject", + "send": "Send", + "to": "To" + }, + "authenticate": { + "please": "Please authenticate", + "please_seed_phrase": "Please authenticate to view seed phrase" + }, + "push_notifications": { + "please_enable_title": "Balance would like to send you push notifications", + "please_enable_body": "It looks like you are using WalletConnect. Please enable push notifications to be notified of transaction requests from WalletConnect." + }, + "assets": { + "no_price": "small balances" + }, + "intro": { + "create_wallet": "Create a Wallet", + "instructions": "Please do not store more in your wallet than you are willing to lose.", + "warning": "This is alpha software.", + "welcome": "Welcome to Balance" + }, + "feedback": { + "cancel": "No thanks", + "choice": "Would you like to manually copy our feedback email address to your clipboard?", + "copy_email_address": "Copy email address", + "email_subject": "📱 Balance Wallet Alpha Feedback", + "error": "Error launching email client", + "send": "Send Feedback" + }, + "loading": { + "error": "There has been an error loading the wallet. Please kill the app and retry.", + "message": "Loading" + }, + "settings": { + "copy_address": "Copy Address", + "copy_seed_phrase": "Copy Seed Phrase", + "hide_seed_phrase": "Hide Seed Phrase", + "show_seed_phrase": "Show Seed Phrase" + }, + "transaction": { + "alert": { + "authentication": "Authentication Failed", + "cancelled_transaction": "Failed to send cancelled transaction to WalletConnect", + "failed_transaction": "Failed to send transaction", + "failed_transaction_status": "Failed to send failed transaction status", + "transaction_status": "Failed to send transaction status" + }, + "confirm": "Confirm transaction", + "request": "Transaction Request", + "send": "Send Transaction" + }, + "unrecognized_qrcode": "Sorry, this QR code could not be recognized.", + "unrecognized_qrcode_title": "Unrecognized QR Code", + "wallet_connect": { + "missing_fcm": "Unable to initialize WalletConnect: missing push notification token. Please try again.", + "error": "Error initializing with WalletConnect" + } + } + } +} diff --git a/src/languages/_french.json b/src/languages/_french.json new file mode 100644 index 00000000..61c878fe --- /dev/null +++ b/src/languages/_french.json @@ -0,0 +1,258 @@ +{ + "translation": { + "homepage": { + "connect_metamask": { + "description": "Connectez-vous au ", + "link_text": "portefeuille navigateur MetaMask", + "link_title": "portefeuille navigateur MetaMask", + "button": "Se connecter à MetaMask" + }, + "connect_ledger": { + "description": "Connectez-vous et signez vos transactions avec votre ", + "link_text": "portefeuille matériel Ledger", + "link_title": "Acheter un hardware wallet Ledger", + "button": "Se connecter à Ledger" + }, + "connect_trezor": { + "description": " Connectez-vous et signez vos transactions avec votre ", + "link_text": "portefeuille matériel Trezor", + "link_title": "Acheter un hardware wallet Trezor", + "button": "Se connecter à Trezor" + }, + "connect_walletconnect": { + "description": "Connectez-vous et signez vos transactions avec votre ", + "link_text": "portefeuille mobile WalletConnect", + "link_title": "portefeuille mobile WalletConnect", + "button": "Utilisez WalletConnect" + }, + "coming_soon": "À venir." + }, + "account": { + "tab_balances": "Soldes", + "tab_collectibles": "Collections", + "tab_transactions": "Transactions", + "tab_interactions": "Interactions", + "tab_uniquetokens": "Tokens uniques", + "tab_balances_tooltip": "Soldes en Ethereum et en tokens", + "tab_transactions_tooltip": "Transactions et transferts de tokens", + "tab_interactions_tooltip": "Interactions de smart-contracts", + "tab_uniquetokens_tooltip": "Tokens uniques", + "label_asset": "Actif", + "label_quantity": "Quantité", + "label_price": "Prix", + "label_24h": "24 h", + "label_status": "État", + "label_total": "Total", + "show": "Afficher", + "hide": "Masquer", + "token": "token", + "tokens": "tokens", + "no_market_value": "avec aucune valeur marchande", + "low_market_value": "avec une faible valeur marchande", + "tx_hash": "Hachage de transaction", + "tx_timestamp": "Horodatage", + "tx_fee": "Frais", + "tx_to": "À", + "tx_from": "De", + "tx_self": "Moi", + "tx_sent": "Envoyé", + "tx_received": "Reçu", + "tx_failed": "Échoué", + "tx_pending": "En attente", + "show_all": "Afficher tout", + "show_less": "Afficher moins", + "unknown_token": "Token inconnu" + }, + "button": { + "connect_walletconnect": "Utiliser WalletConnect", + "exchange": "Échanger", + "send": "Envoyer", + "donate": "Donner de l’ETH", + "receive": "Recevoir", + "close": "Fermer", + "confirm": "Confirmer", + "cancel": "Annuler", + "go_back": "Retourner", + "send_another": "Envoyer un autre", + "exchange_again": "Échanger encore", + "notify_me": "Recevoir des notifications", + "try_again": "Réessayer", + "learn_more": "En savoir plus", + "view": "Affichage" + }, + "input": { + "recipient_address": "Adresse du destinataire", + "private_key": "Clé privée", + "donation_address": "Adresse du Balance Manager", + "asset_amount": "Quantité", + "email": "Adresse de courriel", + "password": "Mot de passe", + "input_text": "Input", + "email_placeholder": "votre@courriel.com", + "password_placeholder": "••••••••••", + "input_placeholder": "Saisissez ici" + }, + "modal": { + "receive_title": "Recevoir à {{walletName}}", + "exchange_title": "Échanger de {{walletName}}", + "donate_title": "Envoyer de {{walletName}} au Balance Manager", + "send_title": "Envoyer de {{walletName}}", + "approve_tx": "Permettre une transaction de {{walletType}}", + "confirm_tx": "Confirmer une transaction de {{walletName}}", + "invalid_address": "Adresse non valide", + "send_max": "Envoyer la valeur max", + "exchange_max": "Échanger la valeur max", + "default_wallet": " Wallet", + "gas_slow": "Lent", + "gas_average": "Moyen", + "gas_fast": "Vite", + "gas_fee": "Frais", + "tx_confirm_sender": "Payeur", + "tx_confirm_recipient": "Bénéficiaire", + "tx_confirm_amount": "Quantité", + "tx_confirm_fee": "Frais de transaction", + "tx_hash": "Hachage de transaction", + "tx_verify": "Vérifiez votre transaction ici", + "deposit_dropdown_label": "Échanger mes", + "withdrawal_dropdown_label": "pour", + "deposit_input_label": "Payer", + "withdrawal_input_label": "Obtenir", + "tx_fee": "Frais de transaction", + "exchange_fee": "Frais d’échange", + "previous_short": "Préc.", + "new": "New", + "helper_balance": "Solde", + "helper_value": "Valeur", + "helper_rate": "Taux", + "helper_price": "Prix", + "helper_min": "Min", + "helper_max": "Max" + }, + "message": { + "web3_not_available": "Veuillez installer l’extension Chrome de MetaMask", + "web3_not_unlocked": "Veuillez déverrouiller votre wallet MetaMask", + "web3_unknown_network": "Réseau inconnu, veuillez basculer vers un autre", + "page_not_found": "404 Page introuvable", + "coming_soon": "À venir...", + "click_to_copy_to_clipboard": "Cliquez ici pour copier dans les presse-papiers", + "failed_request": "Requête échouée, veuillez rafraîchir la page", + "failed_ledger_connection": "Erreur de connexion à Ledger, veuillez vérifier votre appareil", + "failed_trezor_connection": "Erreur de connexion à Trezor, veuillez vérifier votre appareil", + "failed_trezor_popup_blocked": "Veuillez permettre l’affichage de fenêtres contextuelles de Balance", + "no_transactions": "Aucune transaction n’a été trouvée pour ce compte", + "no_unique_tokens": "Aucun token unique n’a été trouvé pour ce compte ", + "no_interactions": "Aucune interaction n’a été trouvée pour ce compte", + "please_connect_ledger": "Veuillez choisir Ethereum après la connexion et le déverrouillage de Ledger", + "please_connect_trezor": "Veuillez vous connecter à Trezor puis suivez les instructions", + "opensea_footer": " est un marché de tokens uniques (non-fongible). En échangeant sur ce marché, on augmente de la valeur de ces tokens. Vous pouvez vendre vos tokens pour de l’argent. Tout cela utilise l’Ethereum. ", + "opensea_header": "Comment est-ce que cela marche « sous le capot ? »", + "power_by": "Réalisé par", + "exchange_not_available": "L’échange n’est pas disponible dans votre région", + "learn_more": "En savoir plus" + }, + "notification": { + "error": { + "no_accounts_found": "Aucun compte Ethereum n’a été trouvé", + "invalid_address": "Adresse non valide, veuillez la vérifier", + "insufficient_balance": "Ce compte a un solde insuffisant", + "insufficient_for_fees": "Solde insuffisant pour payer les frais de transaction", + "invalid_address_scanned": "Adresse non valide, veuillez réessayer", + "invalid_private_key_scanned": "Clé privée non valide, veuillez réessayer", + "failed_scanning_qr_code": "Impossible de scanner le code QR, veuillez réessayer", + "failed_get_gas_prices": "Échec de l’obtention des prix de Gas d’Ethereum", + "failed_get_tx_fee": "Échec de l’estimation des frais de transaction", + "failed_get_account_tx": "Échec de l’obtention des transactions de compte", + "generic_error": "Une erreur s’est produite, veuillez réessayer" + }, + "info": { + "address_copied_to_clipboard": "L’adresse a été copiée dans le presse-papiers" + } + }, + "warning": { + "user_is_offline": "Hors connexion, veuillez vérifier votre connexion Internet", + "user_is_online": "Connexion réussie ! Vous êtes encore connecté à Internet" + }, + "subscribe_form": { + "email_already_subscribed": "Désolé, vous vous êtes déjà inscrit avec cette adresse de courriel", + "too_many_signup_request": "Nous avons détecté trop de demandes d’inscription, veuillez réessayer plus tard", + "successful": "Veuillez consulter vos courriels", + "sending": "En train d’en envoyer...", + "generic_error": "Oups, une erreur s’est produite" + }, + "time": { + "ms": "ms", + "milisecond": "milliseconde", + "miliseconds": "millisecondes", + "s": "s", + "sec": "sec", + "secs": "secs", + "second": "seconde", + "seconds": "secondes", + "min": "min", + "mins": "mins", + "minute": "minute", + "minutes": "minutes", + "hr": "h", + "hrs": "hres", + "hour": "heure", + "hours": "heures", + "day": "jour", + "days": "jours", + "now": "Maintenant" + }, + "wallet": { + "action": { + "cancel": "Annuler", + "paste": "Coller", + "reject": "Refuser", + "send": "Envoyer", + "to": "À" + }, + "authenticate": { + "please": "Veuillez vous authentifier", + "please_seed_phrase": "Veuillez vous authentifier pour afficher votre clé mnémonique" + }, + "assets": { + "no_price": "actifs sans données de prix" + }, + "intro": { + "create_wallet": "Créez un portefeuille", + "instructions": "Veuillez ne pas stocker dans votre portefeuille plus que vous n'êtes prêt à perdre.", + "warning": "Ceci est une version alpha de l'application.", + "welcome": "Bienvenue sur Balance" + }, + "feedback": { + "cancel": "Non merci", + "choice": "Souhaitez-vous copier manuellement l'adresse email de feedback dans votre presse papier ?", + "copy_email_address": "Copier adresse email", + "email_subject": "📱 Feedback pour l'alpha du wallet Balance", + "error": "Erreur lors du chargement de votre client mail", + "send": "Envoyer feedback" + }, + "loading": { + "error": "Une erreur est survenue lors du chargement du portefeuille. Veuillez redémarrer l'application.", + "message": "Chargement" + }, + "settings": { + "copy_address": "Copier adresse", + "copy_seed_phrase": "Copier clé mnémonique", + "hide_seed_phrase": "Cacher clé mnémonique", + "show_seed_phrase": "Afficher clé mnémonique" + }, + "transaction": { + "alert": { + "authentication": "L'authentication a échouée", + "cancelled_transaction": "Échec de l'envoie de la transaction annulée à WalletConnect", + "failed_transaction_status": "Échec de l'envoie du status de la transaction échouée", + "transaction_status": "Échec de l'envoie du status de la transaction" + }, + "confirm": "Confirmer transaction", + "request": "Demande de transaction", + "send": "Envoyer transaction" + }, + "wallet_connect": { + "error": "Erreur d'initialisation avec WalletConnect" + } + } + } +} diff --git a/src/languages/_german.json b/src/languages/_german.json new file mode 100644 index 00000000..1d88068c --- /dev/null +++ b/src/languages/_german.json @@ -0,0 +1,166 @@ +{ + "translation": { + "homepage": { + "connect_metamask": { + "description": "Mit der MetaMask Chrome Erweiterung verbinden.", + "button": "Mit MetaMask verbinden" + }, + "connect_ledger": { + "description": "Verbinden und anmelden mit deiner ", + "link_text": "Ledger Hardware Wallet", + "link_title": "Eine Ledger Hardware Wallet kaufen", + "button": "Mit Ledger verbinden" + }, + "connect_walletconnect": "Verbinden und mit einr WalletConnect-fähigen Mobile Wallet anmelden.", + "coming_soon": "Coming soon…" + }, + "account": { + "tab_balances": "Saldi", + "tab_transactions": "Transaktionen", + "tab_interactions": "Interaktionen", + "tab_balances_tooltip": "Ethereum und Token Saldi", + "tab_transactions_tooltip": "Transaktionen und Tokentransfers", + "tab_interactions_tooltip": "Smart Contract Interaktionen", + "label_asset": "Asset", + "label_quantity": "Menge", + "label_price": "Preis", + "label_24h": "24h", + "label_status": "Status", + "label_total": "Summe", + "show": "Anzeigen", + "hide": "Ausblenden", + "token": "Token", + "tokens": "Tokens", + "no_market_value": "ohne Marktwert", + "low_market_value": "mit geringem Marktwert", + "tx_hash": "Transaktionshash", + "tx_timestamp": "Zeitstempel", + "tx_fee": "Gebühr", + "tx_to": "An", + "tx_from": "Von", + "tx_self": "Ich", + "tx_sent": "Gesendet", + "tx_received": "Erhalten", + "tx_failed": "Fehlgeschlagen", + "tx_pending": "Ausstehend", + "show_all": "Alle anzeigen", + "show_less": "Weniger anzeigen", + "unknown_token": "Unbekannter Token" + }, + "button": { + "connect_walletconnect": "WalletConnect benutzen", + "exchange": "Umwechseln", + "send": "Senden", + "donate": "ETH spenden", + "receive": "Erhalten", + "close": "Schließen", + "cancel": "Abbrechen", + "go_back": "Zurück", + "send_another": "Weitere senden", + "notify_me": "Benachrichtige mich", + "try_again": "Erneut vesuchen" + }, + "input": { + "recipient_address": "Empfängeradresse", + "private_key": "Private-Key", + "donation_address": "Balance Manager Adresse", + "asset_amount": "Menge", + "email": "E-Mail-Adresse", + "password": "Passwort", + "input_text": "Input", + "email_placeholder": "deine@email.de", + "password_placeholder": "••••••••••", + "input_placeholder": "Hier einfügen" + }, + "modal": { + "receive_title": "Mit {{walletName}} erhalten", + "exchange_title": "Umwechseln von {{walletName}}", + "donate_title": "Von {{walletName}} an Balance Manager senden", + "send_title": "Gesendet von {{walletName}}", + "approve_tx": "Transaktion auf {{walletType}} zustimmen", + "confirm_tx": "Transaktion von {{walletName}} bestätigen", + "invalid_address": "Ungültige Adresse", + "send_max": "maximal", + "exchange_max": "maximal", + "default_wallet": " Wallet", + "gas_slow": "Langsam", + "gas_average": "Durchschnittlich", + "gas_fast": "Schnell", + "gas_fee": "Gebühr", + "tx_confirm_sender": "Sender", + "tx_confirm_recipient": "Empfänger", + "tx_confirm_amount": "Menge", + "tx_confirm_fee": "Transaktionsgebühr", + "tx_hash": "Transaktionshash", + "tx_verify": "Transaktion hier verifizieren", + "deposit_dropdown_label": "Tausche mein", + "withdrawal_dropdown_label": "gegen", + "deposit_input_label": "Bezahlen", + "withdrawal_input_label": "Erhalten", + "tx_fee": "Transaktionsgebühr", + "exchange_fee": "Umtauschgebühr" + }, + "message": { + "web3_not_available": "Bitte die MetaMask Chrome Erweiterung installieren", + "web3_not_unlocked": "Bitte die MetaMask Wallet entsperren", + "web3_unknown_network": "Unbekanntes Netzwerk, bitte auf ein anderes wechseln", + "page_not_found": "404 Seite nicht gefunden", + "coming_soon": "Coming soon…", + "click_to_copy_to_clipboard": "Klicken um in die Zwischenablage zu kopieren", + "failed_request": "Anfrage fehlgeschlagen, bitte neu laden", + "failed_ledger_connection": "Fehler beim verbinden zu Ledger, bitte Gerät überprüfen", + "no_transactions": "Keine Transaktionen für diesen Account gefunden", + "no_interactions": "Keine Interaktionen für diesen Account gefunden", + "please_connect_ledger": "Mit Ledger verbinden, entsperren und Ethereum auswählen" + }, + "notification": { + "error": { + "no_accounts_found": "Kein Ethereum Account gefunden", + "invalid_address": "Addresse ist ungültig, bitte überprüfen", + "insufficient_balance": "Nicht genügend Guthaben in diesem Account", + "insufficient_for_fees": "Nicht genügend Guthaben um Transaktionsgebühren zu decken", + "invalid_address_scanned": "Gescannte Adresse ungültig, bitte erneut versuchen", + "invalid_private_key_scanned": "Gescannter Private-Key ungültig, bitte erneut versuchen", + "failed_scanning_qr_code": "QR-Code scannen fehlgeschlagen, bitte erneut versuchen", + "failed_get_gas_prices": "Ethereum Gas Preis ermitteln fehlgeschlagen", + "failed_get_tx_fee": "Transaktionsgebühr ermitteln fehlgeschlagen", + "failed_get_account_tx": "Abrufen der Transaktionen für diesen Account fehlgeschlagen", + "generic_error": "Etwas ist schief gelaufen, bitte erneut versuchen" + }, + "info": { + "address_copied_to_clipboard": "Adresse in Zwischenablage kopiert" + } + }, + "warning": { + "user_is_offline": "Verbindung ist offline, bitte Internetverbindung überprüfen", + "user_is_online": "Verbunden! Wieder online" + }, + "subscribe_form": { + "email_already_subscribed": "Es tut uns Leid, diese E-Mail-Adresse ist bereits in unserem Verteiler", + "too_many_signup_request": "Zu viele Anfragen, bitte später erneut versuchen", + "successful": "Check deine E-Mails", + "sending": "Wird gesendet…", + "generic_error": "Ups, irgendwas ist schief gelaufen" + }, + "time": { + "ms": "ms", + "milisecond": "Millisekunde", + "miliseconds": "Millisekunden", + "s": "s", + "sec": "Sek", + "secs": "Sek", + "second": "Sekunde", + "seconds": "Sekunden", + "min": "Min", + "mins": "Min", + "minute": "Minute", + "minutes": "Minuten", + "hr": "Std", + "hrs": "Stdn", + "hour": "Stunde", + "hours": "Stunden", + "day": "Tag", + "days": "Tage" + } + } +} diff --git a/src/languages/_greek.json b/src/languages/_greek.json new file mode 100644 index 00000000..b27d9fc0 --- /dev/null +++ b/src/languages/_greek.json @@ -0,0 +1,196 @@ +{ + "translation": { + "homepage": { + "connect_metamask": { + "description": "Σύνδεση στην επέκταση Metamask του Chrome.", + "button": "Σύνδεση στο MetaMask" + }, + "connect_ledger": { + "description": "Σύνδεση και πρόσβαση με το ", + "link_text": "hardware πορτοφόλι Ledger", + "link_title": "Αγορά ενός hardware πορτοφολιού Ledger", + "button": "Σύνδεση στο Ledger" + }, + "connect_trezor": { + "description": "Σύνδεση και πρόσβαση με το ", + "link_text": "hardware πορτοφόλι Trezor", + "link_title": "Αγορά ενός hardware πορτοφολιού Trezor", + "button": "Σύνδεση στο Trezor" + }, + "connect_walletconnect": "Σύνδεση με κινητό πορτοφόλι που υποστηρίζει WalletConnect.", + "coming_soon": "Έρχεται σύντομα." + }, + "account": { + "tab_balances": "Υπόλοιπα", + "tab_transactions": "Συναλλαγές", + "tab_interactions": "Αλληλεπιδράσεις", + "tab_uniquetokens": "Μοναδικά Νομίσματα", + "tab_balances_tooltip": "Υπόλοιπα Ethereum και Νομισμάτων", + "tab_transactions_tooltip": "Συναλλαγές και Μεταφορές Νομισμάτων", + "tab_interactions_tooltip": "Αλληλεπιδράσεις Smart Contract", + "tab_uniquetokens_tooltip": "Μοναδικά Νομίσματα", + "label_asset": "Περουσιακό στοιχείο", + "label_quantity": "Ποσότητα", + "label_price": "Τιμή", + "label_24h": "24h", + "label_status": "Κατάσταση", + "label_total": "Σύνολο", + "show": "Εμφάνιση", + "hide": "Απόκρυψη", + "token": "νόμισμα", + "tokens": "νομίσματα", + "no_market_value": "χωρίς εμπορική αξία", + "low_market_value": "με χαμηλή εμπορική αξία", + "tx_hash": "Hash Συναλλαγής", + "tx_timestamp": "Χρονοσήμανση", + "tx_fee": "Τέλος", + "tx_to": "Προς", + "tx_from": "Από", + "tx_self": "Ιδίου", + "tx_sent": "Απεστάλθηκε", + "tx_received": "Ελήφθη", + "tx_failed": "Απέτυχε", + "tx_pending": "Εκκρεμής", + "show_all": "Εμφάνιση όλων", + "show_less": "Εμφάνιση λιγότερων", + "unknown_token": "Άγνωστα Νομίσματα" + }, + "button": { + "connect_walletconnect": "Χρήση WalletConnect", + "exchange": "Μετατροπή", + "send": "Αποστολή", + "donate": "Δωρεά ETH", + "receive": "Λήψη", + "close": "Κλείσιμο", + "confirm": "Επιβεβαίωση", + "cancel": "Ακύρωση", + "go_back": "Πίσω", + "send_another": "Νέα Αποστολή", + "exchange_again": "Νέα Μετατροπή", + "notify_me": "Λήψη ειδοποίησης", + "try_again": "Επανάληψη προσπάθειας", + "learn_more": "Μάθετε περισσότερα", + "view": "Εμφάνιση" + }, + "input": { + "recipient_address": "Διεύθυνση Παραλήπτη", + "private_key": "Ιδιωτικό Κλειδί", + "donation_address": "Διεύθυνση του Balance Manager", + "asset_amount": "Ποσό", + "email": "Email", + "password": "Κωδικός", + "input_text": "Εισαγωγή", + "email_placeholder": "your@email.com", + "password_placeholder": "••••••••••", + "input_placeholder": "Πληκτρολογείστε εδώ" + }, + "modal": { + "receive_title": "Λήψη στο {{walletName}}", + "exchange_title": "Μετατροπή από το {{walletName}}", + "donate_title": "Αποστολή από το {{walletName}} στο Balance Manager", + "send_title": "Αποστολή από το {{walletName}}", + "approve_tx": "Έγκριση συναλλαγής στο {{walletType}}", + "confirm_tx": "Επιβεβαίωση συναλλαγής από το {{walletName}}", + "invalid_address": "Μη Έγκυρη Διεύθυνση", + "send_max": "Αποστολή μεγίστου", + "exchange_max": "Μετατροπή μεγίστου", + "default_wallet": " Πορτοφόλι", + "gas_slow": "Αργά", + "gas_average": "Μέτρια", + "gas_fast": "Γήγορα", + "gas_fee": "Τέλος", + "tx_confirm_sender": "Αποστολέας", + "tx_confirm_recipient": "Παραλήπτης", + "tx_confirm_amount": "Ποσό", + "tx_confirm_fee": "Τέλος Συναλλαγής", + "tx_hash": "Hash Συναλλαγής", + "tx_verify": "Επιβεβαίωση συναλλαγής", + "deposit_dropdown_label": "Μετατροπή", + "withdrawal_dropdown_label": "Σε", + "deposit_input_label": "Πληρωμή", + "withdrawal_input_label": "Λήψη", + "tx_fee": "Τέλος Συναλλαγής", + "exchange_fee": "Τέλος Μετατροπής", + "previous_short": "Προηγ.", + "new": "Νέα", + "helper_balance": "Υπόλοιπο", + "helper_value": "Αξία", + "helper_rate": "Κόστος", + "helper_price": "Price", + "helper_min": "Ελάχιστο", + "helper_max": "Μέγιστο" + }, + "message": { + "web3_not_available": "Παρακαλούμε εγκαταστήστε την επέκταση MetaMask του Chrome", + "web3_not_unlocked": "Παρακαλούμε ξεκλειδώστε το πορτοφόλι MetaMask", + "web3_unknown_network": "Άγνωστο δίκτυο, παρακαλούμε συνδεθείτε σε κάποιο άλλο", + "page_not_found": "404 Η Σελίδα Δε Βρέθηκε", + "coming_soon": "Έρχεται σύντομα...", + "click_to_copy_to_clipboard": "Πάτηστε για αντιγραφή στο πρόχειρο", + "failed_request": "Αποτυχημένο αίτημα, παρακαολούμε ανανεώστε τη σελίδα", + "failed_ledger_connection": "Αποτυχία σύνδεσης στο Ledger, παρακαλούμε ελέγξτε τη συσκευή σας", + "failed_trezor_connection": "Αποτυχία σύνδεσης στο Trezor, παρακαλούμε ελέγξτε τη συσκευή σας", + "failed_trezor_popup_blocked": "Παρακαλούμε επιτρέψτε τα αναδυόμενα παράθυρα για το Balance για να χρησιμοποιήσετε το Trezor σας", + "no_transactions": "Δε βρέθηκαν συναλλαγές για αυτόν το λογαριασμό", + "no_unique_tokens": "Δε βρέθηκαν μοναδικά νομίσματα για αυτόν το λογαριασμό", + "no_interactions": "Δε βρέθηκαν αλληλεπιδράσεις για αυτόν το λογαριασμό", + "please_connect_ledger": "Παρακαλούμε συνδέστε και ξεκλειδώστε το Ledger και επιλέξτε Ethereum", + "please_connect_trezor": "Παρακαλούμε συνδέστε το Trezor σας και ακολουθήστε τις οδηγίες", + "opensea_footer": " μια αγορά για μοναδικά (ή 'μη-ανταλλάξιμα') νομίσματα. Οι κάτοχοι τους τα συναλλάσσονται στην αγορά και αυτό τους δίνει αξία. Μπορείτε να καταθέσετε ως ενέχυρο τα νομίσματα σας για να λάβετε χρήματα. Όλος αυτός ο μηχανισμός τρέχει πάνω στο Ethereum. ", + "opensea_header": "Πώς λειτουργεί αυτός ο μηχανισμός;", + "power_by": "Υποστηρίζεται από", + "exchange_not_available": "Οι μετατροπές δεν είναι διαθέσιμες στην περιοχή σας", + "learn_more": "Μάθετε περισσότερα" + }, + "notification": { + "error": { + "no_accounts_found": "Δε Βρέθηκαν Λογαριασμοί Ethereum", + "invalid_address": "Μη έγκυρη Διεύθυνση, παρακαλούμε ελέγξτε ξανά", + "insufficient_balance": "Ανεπαρκές υπόλοιπο σε αυτόν το λογαριασμό", + "insufficient_for_fees": "Ανεπαρκές υπόλοιπο για την κάλυψη των τελών συναλλαγής", + "invalid_address_scanned": "Μη έγκυση σάρωση Διεύθυνσης, παρακαλούμε προσπαθήστε ξανά", + "invalid_private_key_scanned": "Μη έγκυση σάρωση Ιδιωτικού Κλειδιού, παρακαλούμε προσπαθήστε ξανά", + "failed_scanning_qr_code": "Αποτυχία σάρωσης του QR κώδικα, παρακαλούμε προσπαθήστε ξανά", + "failed_get_gas_prices": "Αποτυχία λήψης των τιμών του Ethereum Gas", + "failed_get_tx_fee": "Αποτυχία εκτίμησης του τέλους συναλλαγής", + "failed_get_account_tx": "Αποτυχία λήψης των συναλλαγών του Λογαριασμού", + "generic_error": "Κατι πήγε στραβά. Παρακαλούμε προσπαθήστε ξανά" + }, + "info": { + "address_copied_to_clipboard": "Η διεύθυνση αντιγράφτηκε στο πρόχειρο" + } + }, + "warning": { + "user_is_offline": "Χωρίς σύνδεση, παρακαλούμε ελέγξτε τη σύνδεση σας στο internet", + "user_is_online": "Επιτυχής σύνδεση! Είστε και πάλι online" + }, + "subscribe_form": { + "email_already_subscribed": "Λυπούμαστε, έχετε εγγραφεί ήδη με αυτή τη διεύθυνση email", + "too_many_signup_request": "Πάρα πολλά αιτήματα εγγραφής, δοκιμάστε ξανά αργότερα", + "successful": "Ελέγξτε το email σας", + "sending": "Αποστολή...", + "generic_error": "Ούπς, κάτι πήγε στραβά" + }, + "time": { + "ms": "ms", + "milisecond": "χιλιοστό", + "miliseconds": "χιλιοστά", + "s": "s", + "sec": "sec", + "secs": "secs", + "second": "δευτερόλπετο", + "seconds": "δευτερόλπετα", + "min": "min", + "mins": "mins", + "minute": "λεπτό", + "minutes": "λεπτά", + "hr": "hr", + "hrs": "hrs", + "hour": "ώρα", + "hours": "ώρες", + "day": "μέρα", + "days": "μέρες", + "now": "Τώρα" + } + } +} \ No newline at end of file diff --git a/src/languages/_italian.json b/src/languages/_italian.json new file mode 100644 index 00000000..6584cae4 --- /dev/null +++ b/src/languages/_italian.json @@ -0,0 +1,166 @@ +{ + "translation": { + "homepage": { + "connect_metamask": { + "description": "Connetti all'estensione di Chrome MetaMask.", + "button": "Connetti a MetaMask" + }, + "connect_ledger": { + "description": "Connetti ed accedi con il tuo ", + "link_text": "Ledger hardware wallet", + "link_title": "Acquista un hardware wallet Ledger", + "button": "Connetti a Ledger" + }, + "connect_walletconnect": "Collegati ed accedi al tuo wallet WalletConnect.", + "coming_soon": "In arrivo." + }, + "account": { + "tab_balances": "Saldo", + "tab_transactions": "Transazioni", + "tab_interactions": "Interazioni", + "tab_balances_tooltip": "Saldo di Ethereum e Token", + "tab_transactions_tooltip": "Transazioni e Transferimenti dei Token", + "tab_interactions_tooltip": "Interazioni di Smart Contracts", + "label_asset": "Asset", + "label_quantity": "Quantità", + "label_price": "Prezzo", + "label_24h": "24h", + "label_status": "Stato", + "label_total": "Totale", + "show": "Mostra", + "hide": "Nascondi", + "token": "token", + "tokens": "token", + "no_market_value": "senza valore di mercato", + "low_market_value": "con basso valore di mercato", + "tx_hash": "Hash della Transazione", + "tx_timestamp": "Data/Ora", + "tx_fee": "Commissioni", + "tx_to": "A", + "tx_from": "Da", + "tx_self": "Io", + "tx_sent": "Inviata", + "tx_received": "Ricevuta", + "tx_failed": "Fallita", + "tx_pending": "In attesa", + "show_all": "Mostra tutto", + "show_less": "Mostra meno", + "unknown_token": "Token Sconosciuto" + }, + "button": { + "connect_walletconnect": "Usa WalletConnect", + "exchange": "Scambia", + "send": "Invia", + "donate": "Dona ETH", + "receive": "Ricevi", + "close": "Chiudi", + "cancel": "Cancella", + "go_back": "Indietro", + "send_another": "Invia un altro", + "notify_me": "Imposta notifica", + "try_again": "Prova ancora" + }, + "input": { + "recipient_address": "Indirizzo Destinatario", + "private_key": "Chiave Privata", + "donation_address": "Indirizzo di Balance Manager", + "asset_amount": "Quantità", + "email": "Email", + "password": "Password", + "input_text": "Input", + "email_placeholder": "tua@email.com", + "password_placeholder": "••••••••••", + "input_placeholder": "Scrivi qui" + }, + "modal": { + "receive_title": "Ricevi a {{walletName}}", + "exchange_title": "Scambia da {{walletName}}", + "donate_title": "Invia da {{walletName}} a Balance Manager", + "send_title": "Invia da {{walletName}}", + "approve_tx": "Approva transazioni su {{walletType}}", + "confirm_tx": "Conferma transazione da {{walletName}}", + "invalid_address": "Indirizzo Non Valido", + "send_max": "Invia max", + "exchange_max": "Scambia max", + "default_wallet": " Wallet", + "gas_slow": "Lento", + "gas_average": "Medio", + "gas_fast": "Veloce", + "gas_fee": "Costi di commissione", + "tx_confirm_sender": "Mittente", + "tx_confirm_recipient": "Destinatario", + "tx_confirm_amount": "Quantità", + "tx_confirm_fee": "Commissioni per la Transazione", + "tx_hash": "Hash della Transazione", + "tx_verify": "Verify your transaction here", + "deposit_dropdown_label": "Scambia i miei", + "withdrawal_dropdown_label": "per", + "deposit_input_label": "Paga", + "withdrawal_input_label": "Ottieni", + "tx_fee": "Commissioni per la Transazione", + "exchange_fee": "Commissioni per il Cambio" + }, + "message": { + "web3_not_available": "Installa l'estensione di Chrome MetaMask", + "web3_not_unlocked": "Per favore sblocca il tuo wallet MetaMask", + "web3_unknown_network": "Network sconosciuto, per favore passa ad un altro", + "page_not_found": "404 Pagina Non Trovata", + "coming_soon": "In arrivo...", + "click_to_copy_to_clipboard": "Clicca per copiare negli appunti", + "failed_request": "Richiesta fallita, per favore ricarica la pagina", + "failed_ledger_connection": "Collegamento a Ledger fallito, controlla il tuo dispositivo", + "no_transactions": "Nessuna transazione trovata per questo account", + "no_interactions": "Nessuna interazione trovata per questo account", + "please_connect_ledger": "Collega e sblocca Ledger, poi seleziona Ethereum" + }, + "notification": { + "error": { + "no_accounts_found": "Nessun Account Ethereum Trovato", + "invalid_address": "Indirizzo non valido, per favore controlla di nuovo", + "insufficient_balance": "Saldo insufficiente per questo account", + "insufficient_for_fees": "Saldo insufficiente per coprire i costi di transazione", + "invalid_address_scanned": "Indirizzo non valido, per favore prova di nuovo", + "invalid_private_key_scanned": "Chiave Privata non valida, per favore prova di nuovo", + "failed_scanning_qr_code": "Scannerizzazione QR code fallita, per favore prova di nuovo", + "failed_get_gas_prices": "Ottenimento prezzi Gas fallito", + "failed_get_tx_fee": "Stima costi di transazione fallita", + "failed_get_account_tx": "Ottenimento transazioni account fallito", + "generic_error": "Qualcosa è andato storto, prova di nuovo" + }, + "info": { + "address_copied_to_clipboard": "Indirizzo copiato negli appunti" + } + }, + "warning": { + "user_is_offline": "Connessione assente, controlla tua connessione internet", + "user_is_online": "Connesso! Sei di nuovo online" + }, + "subscribe_form": { + "email_already_subscribed": "Ci dispiace, ti sei già registrato con questa mail", + "too_many_signup_request": "Troppe richiesta di registrazione, riprova più tardi", + "successful": "Controlla la tua email", + "sending": "Invio...", + "generic_error": "Oops, qualcosa è andato storto" + }, + "time": { + "ms": "ms", + "milisecond": "millisecondo", + "miliseconds": "millisecondi", + "s": "s", + "sec": "sec", + "secs": "sec", + "second": "secondo", + "seconds": "secondi", + "min": "min", + "mins": "min", + "minute": "minuto", + "minutes": "minuti", + "hr": "hr", + "hrs": "hr", + "hour": "ora", + "hours": "ore", + "day": "giorno", + "days": "giorni" + } + } +} diff --git a/src/languages/_polish.json b/src/languages/_polish.json new file mode 100644 index 00000000..a7fb4beb --- /dev/null +++ b/src/languages/_polish.json @@ -0,0 +1,196 @@ +{ + "translation": { + "homepage": { + "connect_metamask": { + "description": "Połącz z rozszerzeniem przegląrki Chrome MetaMask", + "button": "Połącz z MetaMask" + }, + "connect_ledger": { + "description": "Połącz za pomocą ", + "link_text": "Sprzętowego portfela Ledger", + "link_title": "Kup portfel sprzętowy Ledger", + "button": "Połącz z Ledger" + }, + "connect_trezor": { + "description": "Połącz i podpisuj za pomocą ", + "link_text": "Portfel sprzętowy Trezor ", + "link_title": "Kup portfel Trezor ", + "button": "Połącz z Trezor " + }, + "connect_walletconnect": "Podłącz i podpisz za pomocą WalletConnect-wspierającego portfela mobilnego.", + "coming_soon": "Dostępne wkrótce." + }, + "account": { + "tab_balances": "Saldo", + "tab_transactions": "Transakcje", + "tab_interactions": "Interakcje", + "tab_uniquetokens": "Unikalne Tokeny", + "tab_balances_tooltip": "Stany kont Ethereum i tokenów", + "tab_transactions_tooltip": "Transakcje i transfery tokenów", + "tab_interactions_tooltip": "Interakcje z smart contractem", + "tab_uniquetokens_tooltip": "Unikalne tokeny", + "label_asset": "Aktywa", + "label_quantity": "Ilość", + "label_price": "Cena", + "label_24h": "24h", + "label_status": "Status", + "label_total": "Podsumowanie", + "show": "Pokaż", + "hide": "Schowaj", + "token": "token", + "tokens": "tokeny", + "no_market_value": "bez wartości rynkowej", + "low_market_value": "z niewielką wartością rynkową", + "tx_hash": "Hash transakcji", + "tx_timestamp": "Znacznik czasu", + "tx_fee": "Opłata", + "tx_to": "To", + "tx_from": "Od", + "tx_self": "Własna", + "tx_sent": "Wysłano", + "tx_received": "Odebrano", + "tx_failed": "Niepowodzenie", + "tx_pending": "W trakcie", + "show_all": "Pokaż wszystkie", + "show_less": "Pokaż mniej", + "unknown_token": "Unikalny token" + }, + "button": { + "connect_walletconnect": "Użyj WalletConnect", + "exchange": "Wymiana", + "send": "Wyślij", + "donate": "Donacja ETH", + "receive": "Odbierz", + "close": "Zamknij", + "confirm": "Potwierdź", + "cancel": "Anuluj", + "go_back": "Wstecz", + "send_another": "Wyślij kolejną", + "exchange_again": "Wymień ponownie", + "notify_me": "Powiadom mnie", + "try_again": "Spróbuj ponownie", + "learn_more": "Dowiedz się więcej", + "view": "Zobacz" + }, + "input": { + "recipient_address": "Adres odbiorcy", + "private_key": "Prywatny klucz", + "donation_address": "Adres Balance Manager", + "asset_amount": "Ilość", + "email": "Email", + "password": "Hasło", + "input_text": "Wprowadź", + "email_placeholder": "your@email.com", + "password_placeholder": "••••••••••", + "input_placeholder": "Wpisz tutaj" + }, + "modal": { + "receive_title": "Odbierz do{{walletName}}", + "exchange_title": "Wymień z {{walletName}}", + "donate_title": "Wyślij z {{walletName}} do Balance Manager", + "send_title": "Wyślij z {{walletName}}", + "approve_tx": "Zatwierdź transakcję {{walletType}}", + "confirm_tx": "Zatwierdź  transakcję z {{walletName}}", + "invalid_address": "Nieprawdiłowy adres", + "send_max": "Wyślij wszystko", + "exchange_max": "Wymień wszystko", + "default_wallet": " Portfel", + "gas_slow": "Powolnie", + "gas_average": "Średnio", + "gas_fast": "Szybko", + "gas_fee": "Opłata", + "tx_confirm_sender": "Nadawca", + "tx_confirm_recipient": "Odbiorca", + "tx_confirm_amount": "Ilość", + "tx_confirm_fee": "Opłata transakcyjna", + "tx_hash": "Hash transakcji", + "tx_verify": "Zweryfikuj swoją transakcję tutaj", + "deposit_dropdown_label": "Wymień moje", + "withdrawal_dropdown_label": "Na", + "deposit_input_label": "Płać", + "withdrawal_input_label": "Otrzymaj", + "tx_fee": "Opłata transakcyjna", + "exchange_fee": "Opłata wymiany", + "previous_short": "poprzednie.", + "new": "Nowa", + "helper_balance": "Saldo", + "helper_value": "Wartość", + "helper_rate": "Rata", + "helper_price": "Cena", + "helper_min": "Min", + "helper_max": "Max" + }, + "message": { + "web3_not_available": "Proszę zainstalować rozszerzenie Chrome MetaMask", + "web3_not_unlocked": "Proszę odblokować swój portfel MetaMask", + "web3_unknown_network": "Nieznana sieć, prosze zmień na inną", + "page_not_found": "404 Nie znaleziono", + "coming_soon": "Wkrótce ...", + "click_to_copy_to_clipboard": "Skopiuj do schowka", + "failed_request": "Nieudane zapytanie, spróbuj ponownie", + "failed_ledger_connection": "Nieudane połączenie z Ledger, sprawdź urządzenie", + "failed_trezor_connection": "Nieudane połączenie z Trezor, sprawdź urządzenie", + "failed_trezor_popup_blocked": "Włącz pop-up aby używać Trezora z Balance", + "no_transactions": "Nie znaleziono transakcji dla tego konta", + "no_unique_tokens": "Brak unikatowych tokenów", + "no_interactions": "Brak transakcji dla tego konta", + "please_connect_ledger": "Proszę wybierz i podłącz Ledger a następnie wybierz Ethereum", + "please_connect_trezor": "Połącz Trezora i podążaj za instrukcjami", + "opensea_footer": " to rynek unikalnych tokenów. Ich wartość wynika z handlu na rynku. Możesz zastawić swój token aby otrzymać pieniądze. Wszystko działa na sieci Ethereum ", + "opensea_header": "Jak to działa od środka?", + "power_by": "Napędzane przez", + "exchange_not_available": "Wymiana nie jest dostępna w twojej lokalizacji", + "learn_more": "Dowiedz się więcej" + }, + "notification": { + "error": { + "no_accounts_found": "Nie znaleziono kont Ethereum", + "invalid_address": "Adres nieprawidłowy, sprawdź ponownie", + "insufficient_balance": "Niewystarczające saldo na tym koncie", + "insufficient_for_fees": "Niewystarczające saldo na pokrycie opłaty transakcyjnej", + "invalid_address_scanned": "Zeskanowany adres nieprawdiłowy, spróbuj ponownie", + "invalid_private_key_scanned": "Nieprawidłowy klucz prywatny zeskanowany, spróbuj ponownie", + "failed_scanning_qr_code": "Nieudana próba skanu QR, spróbuj jeszcze raz", + "failed_get_gas_prices": "Nie można było pobrać opłat transakcyjnych ethereum (Gas)", + "failed_get_tx_fee": "Nieduana estymacja opłat transakcyjnych", + "failed_get_account_tx": "Nieudane pobranie transakcji", + "generic_error": "Coś się popsuło, spróbuj ponownie" + }, + "info": { + "address_copied_to_clipboard": "Adres skopiowany do schowka" + } + }, + "warning": { + "user_is_offline": "Połączenie zerwane, sprawdź swoją sieć", + "user_is_online": "Połączony! Jesteś online" + }, + "subscribe_form": { + "email_already_subscribed": "Adres już zapisany", + "too_many_signup_request": "Za dużo zapytań, sprawdź póżniej", + "successful": "Sprawdź swój email", + "sending": "Wysyłam...", + "generic_error": "Ups, coś się popsuło" + }, + "time": { + "ms": "ms", + "milisecond": "milisekunda", + "miliseconds": "milisekund", + "s": "s", + "sec": "sekunda", + "secs": "sekund", + "second": "second", + "seconds": "sekundy", + "min": "m", + "mins": "minut", + "minute": "minuta", + "minutes": "minuty", + "hr": "hr", + "hrs": "hrs", + "hour": "godzina", + "hours": "godziny", + "day": "dzień", + "days": "dni", + "now": "Teraz" + } + } +} diff --git a/src/languages/_portuguese.json b/src/languages/_portuguese.json new file mode 100644 index 00000000..d637a38c --- /dev/null +++ b/src/languages/_portuguese.json @@ -0,0 +1,196 @@ +{ + "translation": { + "account": { + "hide": "Ocultar", + "label_24h": "24h", + "label_asset": "Ativos", + "label_price": "Preço", + "label_quantity": "Quantidade", + "label_status": "Status", + "label_total": "Total", + "low_market_value": "baixo valor de mercado", + "no_market_value": "sem valor de mercado", + "show": "Expandir", + "show_all": "Mostrar tudo", + "show_less": "Mostrar menos", + "tab_balances": "Saldos", + "tab_balances_tooltip": "Ethereum e token saldos", + "tab_interactions": "Interações", + "tab_interactions_tooltip": "Interações de Smart Contract", + "tab_transactions": "Transações", + "tab_transactions_tooltip": "Transações e transferências de tokens", + "tab_uniquetokens": "Tokens exclusivos", + "tab_uniquetokens_tooltip": "Tokens exclusivos", + "token": "token", + "tokens": "tokens", + "tx_failed": "Falhou", + "tx_fee": "Taxa", + "tx_from": "De", + "tx_hash": "Hash de transação", + "tx_pending": "Pendente", + "tx_received": "Recebido", + "tx_self": "Eu", + "tx_sent": "Enviado", + "tx_timestamp": "Data/hora", + "tx_to": "Para", + "unknown_token": "Token desconhecido" + }, + "button": { + "cancel": "Cancelar", + "close": "Fechar", + "confirm": "Confirme", + "connect_walletconnect": "Use o WalletConnect", + "donate": "Doe ETH", + "exchange": "Troca", + "exchange_again": "Troque novamente", + "go_back": "Volte", + "learn_more": "Saiba mais", + "notify_me": "Receba notificação", + "receive": "Receber", + "send": "Enviar", + "send_another": "Enviar outro", + "try_again": "Tente novamente", + "view": "Visualizar" + }, + "homepage": { + "coming_soon": "Em breve.", + "connect_ledger": { + "button": "Conecte-se ao Ledger", + "description": "Conecte-se e assine com seu ", + "link_text": "Ledger wallet", + "link_title": "Compre uma carteira de Ledger hardware wallet" + }, + "connect_metamask": { + "button": "Conecte-se ao MetaMask", + "description": "Conecte-se ao MetaMask Chrome extensão." + }, + "connect_trezor": { + "button": "Conecte-se a Trezor", + "description": "Conecte-se e assine com seu ", + "link_text": "Trezor hardware wallet", + "link_title": "Compre uma carteira de Trezor hardware wallet" + }, + "connect_walletconnect": "Ligue e assine com a sua carteira móvel compatível com o WalletConnect." + }, + "input": { + "asset_amount": "Quantidade", + "donation_address": "Balance Manager endereço", + "email": "Email", + "email_placeholder": "seu@email.com", + "input_placeholder": "Digite aqui", + "input_text": "Entrada", + "password": "Senha", + "password_placeholder": "••••••••••", + "private_key": "Chave privada", + "recipient_address": "Endereço do destinatário" + }, + "message": { + "click_to_copy_to_clipboard": "Clique para copiar para a área de transferência", + "coming_soon": "Em breve...", + "exchange_not_available": "O Exchange não está disponível na sua área", + "failed_ledger_connection": "Falha ao conectar-se ao Ledger, verifique seu dispositivo", + "failed_request": "Falha no pedido, por favor atualize", + "failed_trezor_connection": "Falha ao conectar-se ao Trezor, verifique seu dispositivo", + "failed_trezor_popup_blocked": "Por favor, permita que os popups do Balance usem o seu Trezor", + "learn_more": "Saiba mais", + "no_interactions": "Nenhuma interação encontrada para esta conta", + "no_transactions": "Nenhuma transação encontrada para esta conta", + "no_unique_tokens": "Nenhum token exclusivo encontrado para esta conta", + "opensea_footer": "é um mercado para tokens exclusivos (ou 'não fungíveis'). As pessoas negociam no espaço de mercado e isso lhes dá valor. Você pode penhorar seus tokens para conseguir dinheiro. Tudo isso é executado no Ethereum.", + "opensea_header": "Como isso funciona?", + "page_not_found": "404 Página Não Encontrada", + "please_connect_ledger": "Por favor, conecte e desbloqueie o Ledger e selecione Ethereum", + "please_connect_trezor": "Por favor, conecte seu Trezor e siga as instruções", + "power_by": "Distribuído por", + "web3_not_available": "Por favor, instale a extensão MetaMask Chrome", + "web3_not_unlocked": "Por favor, desbloqueie sua carteira MetaMask", + "web3_unknown_network": "Rede desconhecida, por favor mude para outra" + }, + "modal": { + "approve_tx": "Aprovar transação no {{walletType}}", + "confirm_tx": "Confirme a transação de {{walletName}}", + "default_wallet": "Carteira", + "deposit_dropdown_label": "Trocar meu", + "deposit_input_label": "Pagamento", + "donate_title": "Enviar de {{walletName}} para o Gestor de saldo", + "exchange_fee": "Taxa de troca", + "exchange_max": "Troca máxima", + "exchange_title": "Trocar de {{walletName}}", + "gas_average": "Médio", + "gas_fast": "Rápido", + "gas_fee": "Taxa", + "gas_slow": "Lento", + "helper_balance": "Balanço", + "helper_max": "Max", + "helper_min": "Min", + "helper_price": "Preço", + "helper_rate": "Taxa", + "helper_value": "Valor", + "invalid_address": "Endereço inválido", + "new": "Novo", + "previous_short": "Prev.", + "receive_title": "Receber para {{walletName}}", + "send_max": "Enviar max", + "send_title": "Enviar de {{walletName}}", + "tx_confirm_amount": "Montante", + "tx_confirm_fee": "Taxa de transação", + "tx_confirm_recipient": "Destinatário", + "tx_confirm_sender": "Remetente", + "tx_fee": "Taxa de transação", + "tx_hash": "Hash de transação", + "tx_verify": "Verifique sua transação aqui", + "withdrawal_dropdown_label": "Para", + "withdrawal_input_label": "Obter" + }, + "notification": { + "error": { + "failed_get_account_tx": "Falha ao obter transações da conta", + "failed_get_gas_prices": "Não foi possível obter os preços da Ethereum Gas", + "failed_get_tx_fee": "Não foi possível estimar a taxa de transação", + "failed_scanning_qr_code": "Falha ao digitalizar o código QR, por favor, tente novamente", + "generic_error": "Algo deu errado. Por favor tente outra vez", + "insufficient_balance": "Saldo insuficiente nesta conta", + "insufficient_for_fees": "Saldo insuficiente para cobrir as taxas de transação", + "invalid_address": "Endereço inválido, por favor verifique novamente", + "invalid_address_scanned": "Endereço inválido digitalizado, tente novamente", + "invalid_private_key_scanned": "Chave privada inválida, tente novamente", + "no_accounts_found": "Nenhuma Conta Ethereum Encontrada" + }, + "info": { + "address_copied_to_clipboard": "Endereço copiado para a área de transferência" + } + }, + "subscribe_form": { + "email_already_subscribed": "Desculpe, você já se inscreveu com este e-mail", + "generic_error": "Oops, algo deu errado", + "sending": "Enviando ...", + "successful": "Verifique seu e-mail", + "too_many_signup_request": "Muitas solicitações de inscrição, tente novamente mais tarde" + }, + "time": { + "day": "dia", + "days": "dias", + "hour": "hora", + "hours": "horas", + "hr": "hr", + "hrs": "hrs", + "milisecond": "milisegundo", + "miliseconds": "milissegundos", + "min": "min", + "mins": "min", + "minute": "minuto", + "minutes": "minutos", + "ms": "ms", + "now": "Agora", + "s": "s", + "sec": "sec", + "second": "segundo", + "seconds": "segundos", + "secs": "sec" + }, + "warning": { + "user_is_offline": "Conexão offline, por favor, verifique sua conexão com a internet", + "user_is_online": "Conectado! Você está de volta online" + } + } +} diff --git a/src/languages/_russian.json b/src/languages/_russian.json new file mode 100644 index 00000000..5455e9a1 --- /dev/null +++ b/src/languages/_russian.json @@ -0,0 +1,195 @@ +{ + "translation": { + "homepage": { + "connect_metamask": { + "description": "Подключитесь к Chrome плагину MetaMask.", + "button": "Подключиться к MetaMask" + }, + "connect_ledger": { + "description": "Подключите и авторизируйтесь через свой ", + "link_text": "аппаратный кошелек Ledger", + "link_title": "Купить аппаратный кошелек Ledger", + "button": "Подключиться к Ledger" + }, + "connect_trezor": { + "description": "Подключите и авторизируйтесь через свой ", + "link_text": "аппаратный кошелек Trezor", + "link_title": "Купить аппаратный кошелек Trezor", + "button": "Подключиться к Trezor" + }, + "connect_walletconnect": "Подключите и авторизируйстесь через свой мобильный кошелек с поддержкой WalletConnect.", + "coming_soon": "Скоро." + }, + "account": { + "tab_balances": "Балансы", + "tab_transactions": "Транзакции", + "tab_interactions": "Операции", + "tab_uniquetokens": "Уникальные токены", + "tab_balances_tooltip": "Балансы Ethereum и токенов", + "tab_transactions_tooltip": "Транзакции и переводы токенов", + "tab_interactions_tooltip": "Операции со smart контрактами", + "tab_uniquetokens_tooltip": "Уникальные токены", + "label_asset": "Актив", + "label_quantity": "Кол-во", + "label_price": "Цена", + "label_24h": "24ч", + "label_status": "Статус", + "label_total": "Всего", + "show": "Показать", + "hide": "Скрыть", + "token": "токен", + "tokens": "токенов", + "no_market_value": "без рыночной стоимости", + "low_market_value": "с низкой рыночной стоимостью", + "tx_hash": "Хеш транзакции", + "tx_timestamp": "Время", + "tx_fee": "Комиссия", + "tx_to": "На", + "tx_from": "От", + "tx_self": "Self", + "tx_sent": "Отправлено", + "tx_received": "Получено", + "tx_failed": "Ошибка", + "tx_pending": "Ожидается", + "show_all": "Показать все", + "show_less": "Показать меньше", + "unknown_token": "Неизвестный токен" + }, + "button": { + "connect_walletconnect": "Использовать WalletConnect", + "exchange": "Обмен", + "send": "Отправить", + "donate": "Задонатить ETH", + "receive": "Получить", + "close": "Закрыть", + "cancel": "Отменить", + "go_back": "Назад", + "send_another": "Отправить еще", + "exchange_again": "Обменять еще", + "notify_me": "Получить уведомление", + "try_again": "Попробовать еще раз", + "learn_more": "Узнать больше", + "view": "Смотреть" + }, + "input": { + "recipient_address": "Адрес получателя", + "private_key": "Приватный ключ", + "donation_address": "Адрес Balance Manager", + "asset_amount": "Сумма", + "email": "Email", + "password": "Пароль", + "input_text": "Input", + "email_placeholder": "your@email.com", + "password_placeholder": "••••••••••", + "input_placeholder": "Заполнить здесь" + }, + "modal": { + "receive_title": "Получить на {{walletName}}", + "exchange_title": "Обменять с {{walletName}}", + "donate_title": "Отправить с {{walletName}} на Balance Manager", + "send_title": "Отправить с {{walletName}}", + "approve_tx": "Одобрить транзакцию на {{walletType}}", + "confirm_tx": "Подтвердить транзакцию с {{walletName}}", + "invalid_address": "Неправильный адрес", + "send_max": "Отправить макс", + "exchange_max": "Обменять макс", + "default_wallet": " кошелек", + "gas_slow": "Медленно", + "gas_average": "Средне", + "gas_fast": "Быстро", + "gas_fee": "Комиссия", + "tx_confirm_sender": "Отправитель", + "tx_confirm_recipient": "Получатель", + "tx_confirm_amount": "Сумма", + "tx_confirm_fee": "Коммисия транзакции", + "tx_hash": "Хэш транзакции", + "tx_verify": "Верифицируйте свою транзакцию здесь", + "deposit_dropdown_label": "Обменять мои", + "withdrawal_dropdown_label": "на", + "deposit_input_label": "Отдаю", + "withdrawal_input_label": "Получаю", + "tx_fee": "Комиссия транзакции", + "exchange_fee": "Комиссия обмена", + "previous_short": "Пред.", + "new": "Новое", + "helper_balance": "Баланс", + "helper_value": "Стоимость", + "helper_rate": "Курс обмена", + "helper_price": "Цена", + "helper_min": "Мин", + "helper_max": "Макс" + }, + "message": { + "web3_not_available": "Пожалуйста установите Chrome плагин MetaMask", + "web3_not_unlocked": "Пожалуйста разблокируйте свой MetaMask кошелек", + "web3_unknown_network": "Неизвестная сеть, попробуйте подключиться к другой", + "page_not_found": "404 Страница не найдена", + "coming_soon": "Скоро...", + "click_to_copy_to_clipboard": "Нажмите, чтобы скопировать", + "failed_request": "Ошибка запроса, пожалуйста обновите", + "failed_ledger_connection": "Ошибка подключения к Ledger, пожалуйста проверьте устройство", + "failed_trezor_connection": "Ошибка подключения к Trezor, пожалуйста проверьте устройство", + "failed_trezor_popup_blocked": "Пожалуйста разрешите всплывающие окна для Balance чтобы использовать свой Trezor", + "no_transactions": "Не найдено транзакций для этого аккаунта", + "no_unique_tokens": "Не найдено уникальных токенов для этого аккаунта", + "no_interactions": "Не найдено операций с этим аккаунтом", + "please_connect_ledger": "Пожалуйста подключите и разблокируйте Ledger, затем выберите Ethereum", + "please_connect_trezor": "Пожалуйста подключите свой Trezor и следуйте инструкциям", + "opensea_footer": " это marketplace для уникальных (или 'не заменяемых') токенов. Люди торгуют на этом marketplace и это дает им стомость. Вы можете заложить свои токены, чтобы получить деньги. Все это работает на Ethereum. ", + "opensea_header": "Как это работает под капотом?", + "power_by": "Работает на", + "exchange_not_available": "Обмен не доступен в вашем регионе", + "learn_more": "Узнать больше" + }, + "notification": { + "error": { + "no_accounts_found": "Ethereum аккаунт не найден", + "invalid_address": "Неправильный адрес, пожалуйста проверьте еще раз", + "insufficient_balance": "Недостаточный баланс в этом аккаунте", + "insufficient_for_fees": "Недостаточный баланс, чтобы покрыть комиссию транзакции", + "invalid_address_scanned": "Обнаружен неправильный адрес, пожалуйста попробуйте еще раз", + "invalid_private_key_scanned": "Обнаружен неправильный приватный ключ, пожалуйста попробуйте еще раз", + "failed_scanning_qr_code": "Ошибка сканирования QR кода, пожалуйста попробуйте еще раз", + "failed_get_gas_prices": "Не удалось получить Ethereum Gas prices", + "failed_get_tx_fee": "Не удалось посчитать комиссию транзакции", + "failed_get_account_tx": "Не удалось получить транзакции аккаунта", + "generic_error": "Что-то пошло не так, попробуйте еще раз" + }, + "info": { + "address_copied_to_clipboard": "Адрес скопирован в буфер" + } + }, + "warning": { + "user_is_offline": "Нет подключения, пожалуйста проверьте свое интернет-соединение", + "user_is_online": "Подключено! Вы снова онлайн" + }, + "subscribe_form": { + "email_already_subscribed": "Извините, вы уже авторизировались с этим e-mail", + "too_many_signup_request": "Слишком много запросов на регистрацию, пожалуйста попробуйте еще раз", + "successful": "Проверьте свой e-mail", + "sending": "Отправляется...", + "generic_error": "Упс, что-то пошло не так" + }, + "time": { + "ms": "мс", + "milisecond": "милисекунда", + "miliseconds": "милисекунд", + "s": "с", + "sec": "сек", + "secs": "сек", + "second": "секунда", + "seconds": "секунд", + "min": "мин", + "mins": "мин", + "minute": "минута", + "minutes": "минут", + "hr": "ч", + "hrs": "ч", + "hour": "час", + "hours": "часов", + "day": "день", + "days": "дней", + "now": "Сейчас" + } + } +} diff --git a/src/languages/_spanish.json b/src/languages/_spanish.json new file mode 100644 index 00000000..f3028d4c --- /dev/null +++ b/src/languages/_spanish.json @@ -0,0 +1,179 @@ +{ + "translation": { + "homepage": { + "connect_metamask": { + "description": "Conéctate a la extensión de Chrome de Metamask.", + "button": "Conectar MetaMask" + }, + "connect_ledger": { + "description": "Conéctate y firma tus transacciones con tu ", + "link_text": "Ledger Wallet", + "link_title": "Compra un Ledger hardware wallet", + "button": "Conectar Ledger" + }, + "connect_trezor": { + "description": "Conéctate y firma tus transacciones con tu ", + "link_text": "Trezor Wallet", + "link_title": "Compra un Trezor hardware wallet", + "button": "Conectar Trezor" + }, + "connect_walletconnect": { + "description": "Conéctate y firmat con tu wallet móvil de WalletConnect.", + "button": "Conectar WalletConnect" + }, + "coming_soon": "Próximamente." + }, + "account": { + "tab_balances": "Saldos", + "tab_transactions": "Transacciones", + "tab_interactions": "Interacciones", + "tab_balances_tooltip": "Saldos de Ethereum y Tokens", + "tab_transactions_tooltip": "Transacciones y Transferencias de Tokens", + "tab_interactions_tooltip": "Interacciones de Smart Contracts", + "label_asset": "Activo", + "label_quantity": "Cantidad", + "label_price": "Precio", + "label_24h": "24h", + "label_status": "Estado", + "label_total": "Total", + "show": "Mostrar", + "hide": "Esconder", + "token": "token", + "tokens": "tokens", + "no_market_value": "sin valor de mercado", + "low_market_value": "con bajo valor de mercado", + "tx_hash": "Hash de Transacción", + "tx_timestamp": "Fecha/Hora", + "tx_fee": "Tarifa", + "tx_to": "Hacia", + "tx_from": "Desde", + "tx_self": "Yo", + "tx_sent": "Enviado", + "tx_received": "Recibido", + "tx_failed": "Fallido", + "tx_pending": "Pendiente", + "show_all": "Mostrar todo", + "show_less": "Mostrar menos", + "unknown_token": "Token Desconocido" + }, + "button": { + "connect_walletconnect": "Utilizar WalletConnect", + "donate": "Donar ETH", + "exchange": "Cambiar", + "send": "Enviar", + "receive": "Recibir", + "close": "Cerrar", + "cancel": "Cancelar", + "go_back": "Regressar", + "send_another": "Enviar otro", + "notify_me": "Ser notificado", + "try_again": "Intentar otra vez" + }, + "input": { + "recipient_address": "Recipiente Dirección", + "private_key": "Llave Privada", + "asset_amount": "Cantidad", + "donation_address": "Balance Manager Dirección", + "email": "Email", + "password": "Password", + "input_text": "Input", + "email_placeholder": "tu@email.com", + "password_placeholder": "••••••••••", + "input_placeholder": "Escribe aquí" + }, + "modal": { + "receive_title": "Recibir a {{walletName}}", + "exchange_title": "Intercambiar de {{walletName}}", + "send_title": "Enviar de {{walletName}}", + "approve_tx": "Aprobar transacción en {{walletType}}", + "confirm_tx": "Confirmar transacción de {{walletName}}", + "invalid_address": "Dirección inválida", + "send_max": "Enviar máximo", + "exchange_max": "Intercambiar máximo", + "default_wallet": " Cartera", + "gas_slow": "Lento", + "gas_average": "Medio", + "donate_title": "Enviar de {{walletName}} a Balance Manager", + "gas_fast": "Rápido", + "gas_fee": "Tarifa", + "tx_confirm_sender": "Remitente", + "tx_confirm_recipient": "Recipiente", + "tx_confirm_amount": "Cantidad", + "tx_confirm_fee": "Tarifa de transacción", + "tx_hash": "Hash de Transacción", + "tx_verify": "Verifique su transacción aquí", + "deposit_dropdown_label": "Intercambiar mi", + "withdrawal_dropdown_label": "por", + "deposit_input_label": "Pagar", + "withdrawal_input_label": "Obtener", + "tx_fee": "Tarifa de transacción", + "exchange_fee": "Tarifa de cambio" + }, + "message": { + "web3_not_available": "Instala la extension de Chrome de MetaMask", + "web3_not_unlocked": "Por favor desbloquear su cartera MetaMask", + "web3_unknown_network": "Red desconocida, por favor cambie a otro", + "page_not_found": "404 Página no encontrada", + "coming_soon": "Próximamente...", + "click_to_copy_to_clipboard": "Haz clic para copiar al clipboard", + "failed_request": "Solicitud fallida, por favor refrescar", + "failed_ledger_connection": "Error al conectar a Ledger, por favor verifica tu dispositivo", + "failed_trezor_connection": "Error al conectar a Trezor, por favor verifica tu dispositivo", + "no_transactions": "No se encontraron transacciones para esta cuenta", + "no_interactions": "No se encontraron interacciones para esta cuenta", + "please_connect_ledger": "Por favor conecta y desbloquea Ledger y luego selecciona Ethereum", + "please_connect_trezor": "Por favor conecta y desbloquea Trezor y luego selecciona Ethereum", + "exchange_not_available": "Intercambio no está disponible en su área", + "learn_more": "Aprende más" + }, + "notification": { + "error": { + "no_accounts_found": "No se encontraron cuentas de Ethereum", + "invalid_address": "Dirección inválida, verifica nuevamente", + "insufficient_balance": "Saldo insuficiente en esta cuenta", + "insufficient_for_fees": "Saldo insuficiente para cubrir las tarifas de transacción", + "invalid_address_scanned": "Dirección inválida escaneada, intenta nuevamente", + "invalid_private_key_scanned": "Llave privada inválida, intenta nuevamente", + "failed_scanning_qr_code": "Error al escanear el código QR, intenta nuevamente", + "failed_get_gas_prices": "Error al obtener los precios de Gas Ethereum", + "failed_get_tx_fee": "Error al estimar la tarifa de transacción", + "failed_get_account_tx": "Error al obtener transacciones de cuenta", + "generic_error": "Algo salió mal, intenta nuevamente" + }, + "info": { + "address_copied_to_clipboard": "Dirección copiada al clipboard" + } + }, + "warning": { + "user_is_offline": "Conexión offline, por favor verifica tu conexión a Internet", + "user_is_online": "¡Conectado! Estás de vuelta online" + }, + "subscribe_form": { + "email_already_subscribed": "Lo sentimos, ya te has registrado con este correo electrónico", + "too_many_signup_request": "Demasiadas solicitudes de registro, inténtalo de nuevo más tarde", + "successful": "Verifica tu email", + "sending": "Enviando...", + "generic_error": "Uy! Algo salió mal" + }, + "time": { + "ms": "ms", + "milisecond": "milisegundo", + "miliseconds": "milisegundos", + "s": "s", + "sec": "seg", + "secs": "segs", + "second": "segundo", + "seconds": "segundos", + "min": "min", + "mins": "mins", + "minute": "minuto", + "minutes": "minutos", + "hr": "hr", + "hrs": "hrs", + "hour": "hora", + "hours": "horas", + "day": "dia", + "days": "dias" + } + } +} diff --git a/src/languages/index.js b/src/languages/index.js new file mode 100644 index 00000000..9a1bc80e --- /dev/null +++ b/src/languages/index.js @@ -0,0 +1,38 @@ +import i18next from 'i18next'; +import brazilian from './_brazilian.json'; +import english from './_english.json'; +import french from './_french.json'; +import german from './_german.json'; +import czech from './_czech.json'; +import italian from './_italian.json'; +import portuguese from './_portuguese.json'; +import russian from './_russian.json'; +import spanish from './_spanish.json'; +import polish from './_polish.json'; +import greek from './_greek.json'; + +export const resources = { + en: english, + br: brazilian, + de: german, + es: spanish, + fr: french, + it: italian, + cz: czech, + pt: portuguese, + ru: russian, + pl: polish, + el: greek, +}; + +i18next.init({ + lng: 'en', + debug: true, // TODO: only debug in dev mode + resources, +}); + +export const updateLanguage = code => i18next.changeLanguage(code); + +i18next.on('languageChanged', () => {}); + +export default i18next; diff --git a/src/layouts/base.js b/src/layouts/base.js index a24b80b8..15e90496 100644 --- a/src/layouts/base.js +++ b/src/layouts/base.js @@ -2,7 +2,7 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { connect } from 'react-redux'; -import { lang, resources } from 'balance-common'; +import lang, { resources } from '../languages'; import Link from '../components/Link'; import Dropdown from '../components/Dropdown'; import Background from '../components/Background'; diff --git a/src/modals/ApproveTransactionModal/index.js b/src/modals/ApproveTransactionModal/index.js index a3a88cc4..61570a0d 100644 --- a/src/modals/ApproveTransactionModal/index.js +++ b/src/modals/ApproveTransactionModal/index.js @@ -1,6 +1,7 @@ import React from 'react'; -import { capitalize, lang } from 'balance-common'; +import { capitalize } from 'balance-common'; +import lang from '../../languages'; import MetamaskLogo from '../../components/MetamaskLogo'; import LedgerLogo from '../../components/LedgerLogo'; diff --git a/src/modals/DonateModal/index.js b/src/modals/DonateModal/index.js index 9a435af6..7694e641 100644 --- a/src/modals/DonateModal/index.js +++ b/src/modals/DonateModal/index.js @@ -19,11 +19,11 @@ import arrowUp from '../../assets/arrow-up.svg'; import { modalClose } from '../../reducers/_modal'; import { web3SendTransactionMultiWallet } from '../../handlers/web3'; import { notificationShow } from '../../reducers/_notification'; +import lang from '../../languages'; import { capitalize, getEth, - lang, calcTxFee, withSendComponentWithData, } from 'balance-common'; diff --git a/src/modals/ExchangeModal/index.js b/src/modals/ExchangeModal/index.js index f4b470a8..564f67b6 100644 --- a/src/modals/ExchangeModal/index.js +++ b/src/modals/ExchangeModal/index.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../../languages'; import Card from '../../components/Card'; import Input from '../../components/Input'; import LineBreak from '../../components/LineBreak'; diff --git a/src/modals/ReceiveModal/index.js b/src/modals/ReceiveModal/index.js index ce4eeab0..dfe20e61 100644 --- a/src/modals/ReceiveModal/index.js +++ b/src/modals/ReceiveModal/index.js @@ -3,7 +3,8 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import styled from 'styled-components'; -import { capitalize, lang } from 'balance-common'; +import { capitalize } from 'balance-common'; +import lang from '../../languages'; import Card from '../../components/Card'; import Button from '../../components/Button'; diff --git a/src/modals/SendModal/index.js b/src/modals/SendModal/index.js index 5429b78d..0d7d6b39 100644 --- a/src/modals/SendModal/index.js +++ b/src/modals/SendModal/index.js @@ -19,10 +19,10 @@ import arrowUp from '../../assets/arrow-up.svg'; import qrIcon from '../../assets/qr-code-bnw.png'; import { modalClose } from '../../reducers/_modal'; +import lang from '../../languages'; import { calcTxFee, capitalize, - lang, withSendComponentWithData, } from 'balance-common'; import { web3SendTransactionMultiWallet } from '../../handlers/web3'; diff --git a/src/modals/SuccessModal/index.js b/src/modals/SuccessModal/index.js index f228189d..4895b66d 100644 --- a/src/modals/SuccessModal/index.js +++ b/src/modals/SuccessModal/index.js @@ -1,5 +1,5 @@ import React from 'react'; -import { lang } from 'balance-common'; +import lang from '../../languages'; import Button from '../../components/Button'; import arrowUp from '../../assets/arrow-up.svg'; import { diff --git a/src/modals/WalletConnectModal.js b/src/modals/WalletConnectModal.js index 8fabc910..b76809ba 100644 --- a/src/modals/WalletConnectModal.js +++ b/src/modals/WalletConnectModal.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../languages'; import Card from '../components/Card'; import Loader from '../components/Loader'; import QRCodeDisplay from '../components/QRCodeDisplay'; diff --git a/src/pages/Ledger.js b/src/pages/Ledger.js index 7d00e849..c465e079 100644 --- a/src/pages/Ledger.js +++ b/src/pages/Ledger.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../languages'; import HelpSvg from '../assets/help.svg'; import BaseLayout from '../layouts/base'; import Card from '../components/Card'; diff --git a/src/pages/Metamask.js b/src/pages/Metamask.js index 911fcb31..69967493 100644 --- a/src/pages/Metamask.js +++ b/src/pages/Metamask.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../languages'; import BaseLayout from '../layouts/base'; import Account from '../views/Account'; import Card from '../components/Card'; diff --git a/src/pages/NotFound.js b/src/pages/NotFound.js index 37ee5e15..875ac5ee 100644 --- a/src/pages/NotFound.js +++ b/src/pages/NotFound.js @@ -1,6 +1,6 @@ import React from 'react'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../languages'; import BaseLayout from '../layouts/base'; import Link from '../components/Link'; diff --git a/src/pages/Trezor.js b/src/pages/Trezor.js index dc54ee73..57596749 100644 --- a/src/pages/Trezor.js +++ b/src/pages/Trezor.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../languages'; import HelpSvg from '../assets/help.svg'; import BaseLayout from '../layouts/base'; import Card from '../components/Card'; diff --git a/src/pages/Wallet.js b/src/pages/Wallet.js index 82f0890f..27f63e98 100644 --- a/src/pages/Wallet.js +++ b/src/pages/Wallet.js @@ -5,7 +5,7 @@ import styled from 'styled-components'; import BaseLayout from '../layouts/base'; import Account from '../views/Account'; import Card from '../components/Card'; -import { lang } from 'balance-common'; +import lang from '../languages'; import { walletConnectInit } from '../reducers/_walletconnect'; import { fonts, colors } from '../styles'; diff --git a/src/pages/index.js b/src/pages/index.js index 840840be..769851a4 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -2,7 +2,8 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { connect } from 'react-redux'; -import { accountInitializeState, lang } from 'balance-common'; +import lang from '../languages'; +import { accountInitializeState } from 'balance-common'; import Link from '../components/Link'; import BaseLayout from '../layouts/base'; import Card from '../components/Card'; diff --git a/src/reducers/_ledger.js b/src/reducers/_ledger.js index 0fb9156f..fa181e5a 100644 --- a/src/reducers/_ledger.js +++ b/src/reducers/_ledger.js @@ -1,4 +1,4 @@ -import { lang } from 'balance-common'; +import lang from '../languages'; import { accountUpdateAccountAddress, accountUpdateNetwork, diff --git a/src/reducers/_trezor.js b/src/reducers/_trezor.js index 9407c1b0..e6460953 100644 --- a/src/reducers/_trezor.js +++ b/src/reducers/_trezor.js @@ -1,4 +1,4 @@ -import { lang } from 'balance-common'; +import lang from '../languages'; import { accountUpdateAccountAddress, accountUpdateNetwork, diff --git a/src/reducers/_warning.js b/src/reducers/_warning.js index 9928c9f5..8f71c980 100644 --- a/src/reducers/_warning.js +++ b/src/reducers/_warning.js @@ -1,4 +1,4 @@ -import { lang } from 'balance-common'; +import lang from '../languages'; // -- Constants ------------------------------------------------------------- // const WARNING_PARSE = 'warning/WARNING_PARSE'; diff --git a/src/views/Account/AccountBalances.js b/src/views/Account/AccountBalances.js index e4b1f573..3ec4d09c 100644 --- a/src/views/Account/AccountBalances.js +++ b/src/views/Account/AccountBalances.js @@ -4,12 +4,12 @@ import PropTypes from 'prop-types'; import styled from 'styled-components'; import AssetIcon from '../../components/AssetIcon'; import ToggleIndicator from '../../components/ToggleIndicator'; +import lang from '../../languages'; import { convertStringToNumber, ellipseText, hasHighMarketValue, hasLowMarketValue, - lang, } from 'balance-common'; import { colors, fonts, responsive } from '../../styles'; diff --git a/src/views/Account/AccountTransactions.js b/src/views/Account/AccountTransactions.js index 64d1707b..3aa44681 100644 --- a/src/views/Account/AccountTransactions.js +++ b/src/views/Account/AccountTransactions.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import { connect } from 'react-redux'; -import { lang } from 'balance-common'; +import lang from '../../languages'; import Card from '../../components/Card'; import ButtonCustom from '../../components/ButtonCustom'; import LineBreak from '../../components/LineBreak'; diff --git a/src/views/Account/AccountUniqueTokens.js b/src/views/Account/AccountUniqueTokens.js index 722d1ba1..294d69e5 100644 --- a/src/views/Account/AccountUniqueTokens.js +++ b/src/views/Account/AccountUniqueTokens.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import styled from 'styled-components'; -import { lang } from 'balance-common'; +import lang from '../../languages'; import Card from '../../components/Card'; import Footer from '../../components/Footer'; import UniqueToken from '../../components/UniqueToken'; diff --git a/src/views/Account/index.js b/src/views/Account/index.js index 03ebfeec..40913d8a 100644 --- a/src/views/Account/index.js +++ b/src/views/Account/index.js @@ -4,7 +4,8 @@ import { withRouter } from 'react-router-dom'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import styled from 'styled-components'; -import { capitalize, lang } from 'balance-common'; +import lang from '../../languages'; +import { capitalize } from 'balance-common'; import TabMenu from '../../components/TabMenu'; import Card from '../../components/Card'; import Button from '../../components/Button'; diff --git a/yarn.lock b/yarn.lock index d3cfd4e9..c3f351e5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -977,7 +977,6 @@ "@walletconnect/browser@^1.0.0-beta.18": version "1.0.0-beta.18" resolved "https://registry.yarnpkg.com/@walletconnect/browser/-/browser-1.0.0-beta.18.tgz#97af55422c3d0943ba512c91d2cb541bd8403bc2" - integrity sha512-2dXIGQxUL0LCAIwIOIpZ8Hemkx4V6qdmRrgtDuaaAgmbMoh45N90e+38jH4wY5T0psITT7BP9Y5ceGLgAz8c+Q== dependencies: "@walletconnect/core" "^1.0.0-beta.18" "@walletconnect/types" "^1.0.0-beta.17" @@ -986,7 +985,6 @@ "@walletconnect/core@^1.0.0-beta.18": version "1.0.0-beta.18" resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-1.0.0-beta.18.tgz#b43d5d79bb6b7beaca1bf42b0d2500f04fcd3815" - integrity sha512-xWs9WkXFlTwSCkUzqXVF49BtCHwtzyAQL9x1MCFWTSGlqK8xapSH+2PEq4k2CHw/Zjh22oK/qBvuYgCdo+RFuw== dependencies: "@walletconnect/types" "^1.0.0-beta.17" "@walletconnect/utils" "^1.0.0-beta.18" @@ -994,7 +992,6 @@ "@walletconnect/qrcode-modal@^1.0.0-beta.18": version "1.0.0-beta.18" resolved "https://registry.yarnpkg.com/@walletconnect/qrcode-modal/-/qrcode-modal-1.0.0-beta.18.tgz#69da4665b0ac3a2b4c171f2adf72005295739bf6" - integrity sha512-IBQZf4jo3Iw7bmcTzfpv1eq70+c+rgjxVecg8KzkExf7ivfDcrykkKh0NTWXpHbbWLc2f6YSlC9yePqKydwAFA== dependencies: "@walletconnect/types" "^1.0.0-beta.17" qr-image "^3.2.0" @@ -1002,12 +999,10 @@ "@walletconnect/types@^1.0.0-beta.17": version "1.0.0-beta.18" resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-1.0.0-beta.18.tgz#c258413d41e4c557b68cef5d22ff463c3b1cd0d6" - integrity sha512-cqhVcyNdXEDvnUUD8r7cWI6ZHeIsIOriJMIkOuxslWbaHadnJ0SGStJ9sSINibAMD/ZUEabQZRWA18BxL4jRvQ== "@walletconnect/utils@^1.0.0-beta.18": version "1.0.0-beta.18" resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-1.0.0-beta.18.tgz#077569c9c7e525ec8a50f4014eb662ca33694cec" - integrity sha512-IgpJyHmtgRmset5gZZ9gTGziZQ5fKWtTud41vosKtQhgvh8+HQkPVWPqj/KxQupLFWK3/ACo1CqlxoKAjctpOQ== dependencies: "@walletconnect/types" "^1.0.0-beta.17" js-sha3 "^0.8.0" @@ -2099,6 +2094,21 @@ backoff@^2.5.0: dependencies: precond "0.2" +balance-common@../balance-common/: + version "0.6.1" + dependencies: + axios "^0.18.0" + bignumber.js "^7.0.1" + date-fns "^1.29.0" + i18next "^11.3.2" + jsonp "^0.2.1" + lodash "^4.17.10" + react "^16.2.0" + react-redux "^5.0.7" + redux "^4.0.0" + underscore "^1.9.1" + web3 "1.0.0-beta.34" + balance-common@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/balance-common/-/balance-common-0.6.1.tgz#8953a6d3ad83dde0d736b7128e042c454e92fb9e" @@ -6592,7 +6602,6 @@ js-sha3@^0.7.0: js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -7129,7 +7138,6 @@ lodash.debounce@^4.0.8: lodash.isnumber@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" - integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= lodash.memoize@^4.1.2: version "4.1.2" @@ -9156,7 +9164,6 @@ q@^1.1.2: qr-image@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/qr-image/-/qr-image-3.2.0.tgz#9fa8295beae50c4a149cf9f909a1db464a8672e8" - integrity sha1-n6gpW+rlDEoUnPn5CaHbRkqGcug= qrcode@^1.2.0: version "1.3.2" From 17504a8366461987b153ffda2dff2a6e604f76cc Mon Sep 17 00:00:00 2001 From: Jack Leslie Date: Mon, 3 Jun 2019 00:13:33 +0100 Subject: [PATCH 2/4] Added reducers, helpers, handlers and component(s), fixed almost all imports --- src/components/CopyToClipboard/index.js | 2 +- src/components/DropdownAsset.js | 2 +- src/components/SendComponentWithData.js | 322 +++++++++ src/components/SubscribeForm.js | 2 +- src/handlers/api.js | 164 +++++ src/handlers/commonStorage.js | 272 +++++++ src/handlers/ledger-eth.js | 5 +- src/handlers/localstorage.js | 2 +- src/handlers/opensea-api.js | 30 + src/handlers/parsers.js | 751 ++++++++++++++++++++ src/handlers/web3.js | 252 +++++++ src/helpers/bignumber.js | 505 +++++++++++++ src/helpers/time.js | 195 +++++ src/helpers/utilities.js | 198 ++++++ src/helpers/validators.js | 27 + src/index.js | 2 +- src/layouts/base.js | 4 +- src/modals/ApproveTransactionModal/index.js | 2 +- src/modals/DonateModal/index.js | 9 +- src/modals/ExchangeModal/index.js | 6 +- src/modals/ReceiveModal/index.js | 2 +- src/modals/SendModal/index.js | 7 +- src/modals/index.js | 2 +- src/pages/index.js | 2 +- src/reducers/_account.js | 727 +++++++++++++++++++ src/reducers/_exchange.js | 6 +- src/reducers/_ledger.js | 2 +- src/reducers/_metamask.js | 4 +- src/reducers/_send.js | 498 +++++++++++++ src/reducers/_trezor.js | 2 +- src/reducers/_walletconnect.js | 2 +- src/reducers/index.js | 3 +- src/views/Account/AccountBalances.js | 4 +- src/views/Account/AccountTransactions.js | 6 +- src/views/Account/index.js | 2 +- 35 files changed, 3978 insertions(+), 43 deletions(-) create mode 100644 src/components/SendComponentWithData.js create mode 100644 src/handlers/api.js create mode 100644 src/handlers/commonStorage.js create mode 100644 src/handlers/opensea-api.js create mode 100644 src/handlers/parsers.js create mode 100644 src/helpers/bignumber.js create mode 100644 src/helpers/time.js create mode 100644 src/helpers/utilities.js create mode 100644 src/helpers/validators.js create mode 100644 src/reducers/_account.js create mode 100644 src/reducers/_send.js diff --git a/src/components/CopyToClipboard/index.js b/src/components/CopyToClipboard/index.js index 6e8869fa..ff438661 100644 --- a/src/components/CopyToClipboard/index.js +++ b/src/components/CopyToClipboard/index.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import lang from '../../languages'; import clipboardIcon from '../../assets/clipboard.png'; -import { toChecksumAddress } from 'balance-common'; +import { toChecksumAddress } from '../../handlers/web3'; import { notificationShow } from '../../reducers/_notification'; import { StyledCopyToClipboard, diff --git a/src/components/DropdownAsset.js b/src/components/DropdownAsset.js index b5c6f268..6bd29927 100644 --- a/src/components/DropdownAsset.js +++ b/src/components/DropdownAsset.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import selector from '../assets/selector-grey.svg'; -import { ellipseText } from 'balance-common'; +import { ellipseText } from '../helpers/utilities'; import { fonts, colors, shadows, responsive, transitions } from '../styles'; import AssetIcon from './AssetIcon'; import ClickOutside from './ClickOutside'; diff --git a/src/components/SendComponentWithData.js b/src/components/SendComponentWithData.js new file mode 100644 index 00000000..108e7a39 --- /dev/null +++ b/src/components/SendComponentWithData.js @@ -0,0 +1,322 @@ +import React, { Component } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { get } from 'lodash'; +import lang from '../languages'; +import { + sendModalInit, + sendUpdateGasPrice, + sendTransaction, + sendClearFields, + sendUpdateRecipient, + sendUpdateNativeAmount, + sendUpdateAssetAmount, + sendUpdateSelected, + sendMaxBalance, + sendToggleConfirmationView, +} from '../reducers/_send'; +import { notificationShow } from '../reducers/_notification'; + +import { isValidAddress } from '../helpers/validators'; +import { convertAmountFromBigNumber, greaterThan } from '../helpers/bignumber'; + +import { transactionData } from '../helpers/utilities'; + +const reduxProps = ({ send, account }) => ({ + fetching: send.fetching, + recipient: send.recipient, + nativeAmount: send.nativeAmount, + assetAmount: send.assetAmount, + isSufficientGas: send.isSufficientGas, + isSufficientBalance: send.isSufficientBalance, + txHash: send.txHash, + address: send.address, + selected: send.selected, + gasPrices: send.gasPrices, + gasPrice: send.gasPrice, + gasLimit: send.gasLimit, + gasPriceOption: send.gasPriceOption, + confirm: send.confirm, + accountInfo: account.accountInfo, + accountType: account.accountType, + network: account.network, + nativeCurrency: account.nativeCurrency, + prices: account.prices, +}); + +/** + * Create SendComponent connected to redux with actions for sending assets. + * @param {Component} SendComponent React component for sending. + * @param {Object} options + * {Function} options.sendTransactionCallback Function to be run after sendTransaction redux action. + * {String} options.defaultAsset Symbol for default asset to send. + * @return {Component} SendComponent connected to redux. + */ +export const withSendComponentWithData = (SendComponent, options) => { + class SendComponentWithData extends Component { + static propTypes = { + sendModalInit: PropTypes.func.isRequired, + sendUpdateGasPrice: PropTypes.func.isRequired, + sendTransaction: PropTypes.func.isRequired, + sendClearFields: PropTypes.func.isRequired, + sendUpdateRecipient: PropTypes.func.isRequired, + sendUpdateNativeAmount: PropTypes.func.isRequired, + sendUpdateAssetAmount: PropTypes.func.isRequired, + sendUpdateSelected: PropTypes.func.isRequired, + sendMaxBalance: PropTypes.func.isRequired, + sendToggleConfirmationView: PropTypes.func.isRequired, + notificationShow: PropTypes.func.isRequired, + fetching: PropTypes.bool.isRequired, + recipient: PropTypes.string.isRequired, + nativeAmount: PropTypes.string.isRequired, + assetAmount: PropTypes.string.isRequired, + isSufficientGas: PropTypes.func.isRequired, + isSufficientBalance: PropTypes.func.isRequired, + txHash: PropTypes.string.isRequired, + selected: PropTypes.object.isRequired, + gasPrice: PropTypes.object.isRequired, + gasPrices: PropTypes.object.isRequired, + gasLimit: PropTypes.number.isRequired, + gasPriceOption: PropTypes.string.isRequired, + confirm: PropTypes.bool.isRequired, + accountInfo: PropTypes.object.isRequired, + accountType: PropTypes.string.isRequired, + network: PropTypes.string.isRequired, + nativeCurrency: PropTypes.string.isRequired, + prices: PropTypes.object.isRequired, + }; + + constructor(props) { + super(props); + + this.state = { + isValidAddress: false, + showQRCodeReader: false, + }; + + this.defaultAsset = options.defaultAsset || 'ETH'; + this.gasFormat = options.gasFormat || 'long'; + this.sendTransactionCallback = + options.sendTransactionCallback || function noop() {}; + } + + componentDidMount() { + this.props.sendModalInit({ + defaultAsset: this.defaultAsset, + gasFormat: this.gasFormat, + }); + } + + componentDidUpdate(prevProps) { + const { + assetAmount, + recipient, + selected, + sendUpdateGasPrice, + } = this.props; + + if (recipient.length >= 42) { + if (selected.symbol !== prevProps.selected.symbol) { + sendUpdateGasPrice(); + } else if (recipient !== prevProps.recipient) { + sendUpdateGasPrice(); + } else if (assetAmount !== prevProps.assetAmount) { + sendUpdateGasPrice(); + } + } + + if (recipient !== prevProps.recipient) { + this.setState({ isValidAddress: isValidAddress(recipient) }); + } + } + + onAddressInputFocus = () => { + const { recipient } = this.props; + + this.setState({ isValidAddress: isValidAddress(recipient) }); + }; + + onAddressInputBlur = () => { + const { recipient } = this.props; + + this.setState({ isValidAddress: isValidAddress(recipient) }); + }; + + onGoBack = () => this.props.sendToggleConfirmationView(false); + + onSendMaxBalance = () => this.props.sendMaxBalance(); + + onSendAnother = () => { + this.props.sendToggleConfirmationView(false); + this.props.sendClearFields(); + this.props.sendModalInit({ defaultAsset: this.defaultAsset }); + }; + + onSubmit = event => { + if (event && typeof event.preventDefault === 'function') { + event.preventDefault(); + } + + if (!this.props.gasPrice.txFee) { + this.props.notificationShow( + lang.t('notification.error.generic_error'), + true, + ); + + return; + } + + if (!this.props.confirm) { + if (!isValidAddress(this.props.recipient)) { + this.props.notificationShow( + lang.t('notification.error.invalid_address'), + true, + ); + + return; + } else if (this.props.selected.symbol === 'ETH') { + const { requestedAmount, balance, amountWithFees } = transactionData( + this.props.accountInfo, + this.props.assetAmount, + this.props.gasPrice, + ); + + if (greaterThan(requestedAmount, balance)) { + this.props.notificationShow( + lang.t('notification.error.insufficient_balance'), + true, + ); + + return; + } else if (greaterThan(amountWithFees, balance)) { + this.props.notificationShow( + lang.t('notification.error.insufficient_for_fees'), + true, + ); + + return; + } + } else { + const { requestedAmount, balance, txFee } = transactionData( + this.props.accountInfo, + this.props.assetAmount, + this.props.gasPrice, + ); + + const tokenBalanceAmount = get(this.props, 'selected.balance.amount'); + const tokenBalance = convertAmountFromBigNumber(tokenBalanceAmount); + + if (greaterThan(requestedAmount, tokenBalance)) { + this.props.notificationShow( + lang.t('notification.error.insufficient_balance'), + true, + ); + + return; + } else if (greaterThan(txFee, balance)) { + this.props.notificationShow( + lang.t('notification.error.insufficient_for_fees'), + true, + ); + + return; + } + } + + this.props.sendToggleConfirmationView(true); + + return this.props.sendTransaction( + { + address: this.props.accountInfo.address, + recipient: this.props.recipient, + amount: this.props.assetAmount, + asset: this.props.selected, + gasPrice: this.props.gasPrice, + gasLimit: this.props.gasLimit, + }, + this.sendTransactionCallback, + ); + } + }; + + updateGasPrice = gasPrice => { + this.props.sendUpdateGasPrice(gasPrice); + }; + + onClose = () => { + this.props.sendClearFields(); + // TODO: close function ?? (previously was to hit modal reducer) + }; + + updateGasPrice = gasPrice => { + this.props.sendUpdateGasPrice(gasPrice); + }; + + // QR Code Reader Handlers + toggleQRCodeReader = () => + this.setState({ showQRCodeReader: !this.state.showQRCodeReader }); + + onQRCodeValidate = rawData => { + const data = rawData.match(/0x\w{40}/g) + ? rawData.match(/0x\w{40}/g)[0] + : null; + const result = data ? isValidAddress(data) : false; + const onError = () => + this.props.notificationShow( + lang.t('notification.error.invalid_address_scanned'), + true, + ); + + return { data, result, onError }; + }; + + onQRCodeScan = data => { + this.props.sendUpdateRecipient(data); + this.setState({ showQRCodeReader: false }); + }; + + onQRCodeError = () => { + this.props.notificationShow( + lang.t('notification.error.failed_scanning_qr_code'), + true, + ); + }; + + render() { + return ( + + ); + } + } + + return connect( + reduxProps, + { + sendModalInit, + sendUpdateGasPrice, + sendTransaction, + sendClearFields, + sendUpdateRecipient, + sendUpdateNativeAmount, + sendUpdateAssetAmount, + sendUpdateSelected, + sendMaxBalance, + sendToggleConfirmationView, + notificationShow, + }, + )(SendComponentWithData); +}; diff --git a/src/components/SubscribeForm.js b/src/components/SubscribeForm.js index 8d186fb6..1ddaead7 100644 --- a/src/components/SubscribeForm.js +++ b/src/components/SubscribeForm.js @@ -3,7 +3,7 @@ import styled from 'styled-components'; import PropTypes from 'prop-types'; import jsonp from 'jsonp'; import Button from './Button'; -import { isValidEmail } from 'balance-common'; +import { isValidEmail } from '../helpers/validators'; import lang from '../languages'; import { fonts, colors, transitions } from '../styles'; diff --git a/src/handlers/api.js b/src/handlers/api.js new file mode 100644 index 00000000..86b64ee8 --- /dev/null +++ b/src/handlers/api.js @@ -0,0 +1,164 @@ +import axios from 'axios'; +import { findIndex, slice } from 'lodash'; +import { + parseAccountAssets, + parseAccountTransactions, + parseHistoricalTransactions, +} from './parsers'; +import { formatInputDecimals } from '../helpers/bignumber'; +import nativeCurrencies from '../references/native-currencies.json'; + +/** + * @desc get single asset price + * @param {String} [asset=''] + * @param {String} [native='USD'] + * @return {Promise} + */ +export const apiGetSinglePrice = (asset = '', native = 'USD') => { + return cryptocompare.get( + `/price?fsym=${asset}&tsyms=${native}&apiKey=${ + process.env.REACT_APP_CRYPTOCOMPARE_API_KEY + }`, + ); +}; + +/** + * Configuration for cryptocompare api + * @type axios instance + */ +const cryptocompare = axios.create({ + baseURL: 'https://min-api.cryptocompare.com/data/', + timeout: 30000, // 30 secs + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, +}); + +/** + * @desc get all assets prices + * @param {Array} [asset=[]] + * @return {Promise} + */ +export const apiGetPrices = (assets = []) => { + const assetsQuery = JSON.stringify(assets).replace(/[[\]"]/gi, ''); + const nativeQuery = JSON.stringify(Object.keys(nativeCurrencies)).replace( + /[[\]"]/gi, + '', + ); + return cryptocompare.get( + `/pricemultifull?fsyms=${assetsQuery}&tsyms=${nativeQuery}`, + ); +}; + +/** + * @desc get historical prices + * @param {String} [assetSymbol=''] + * @param {Number} [timestamp=Date.now()] + * @return {Promise} + */ +export const apiGetHistoricalPrices = ( + assetSymbol = '', + timestamp = Date.now(), // TODO error: timestamp would be ms +) => { + const nativeQuery = JSON.stringify(Object.keys(nativeCurrencies)).replace( + /[[\]"]/gi, + '', + ); + return cryptocompare.get( + `/pricehistorical?fsym=${assetSymbol}&tsyms=${nativeQuery}&ts=${timestamp}&apiKey=${ + process.env.REACT_APP_CRYPTOCOMPARE_API_KEY + }`, + ); +}; + +/** + * Configuration for balance api + * @type axios instance + */ +const api = axios.create({ + baseURL: 'https://indexer.balance.io', + timeout: 30000, // 30 secs + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, +}); + +/** + * @desc get account balances + * @param {String} [address = ''] + * @param {String} [network = 'mainnet'] + * @return {Promise} + */ +export const apiGetAccountBalances = async ( + address = '', + network = 'mainnet', +) => { + try { + const { data } = await api.get(`/get_balances/${network}/${address}`); + const accountInfo = parseAccountAssets(data, address); + const result = { data: accountInfo }; + return result; + } catch (error) { + console.log('Error getting acct balances from proxy', error); + throw error; + } +}; + +/** + * @desc get transaction data + * @param {String} [address = ''] + * @param {String} [network = 'mainnet'] + * @param {Number} [page = 1] + * @return {Promise} + */ +export const apiGetTransactionData = ( + address = '', + network = 'mainnet', + page = 1, +) => api.get(`/get_transactions/${network}/${address}/${page}`); + +/** + * @desc get account transactions + * @param {String} [address = ''] + * @param {String} [network = 'mainnet'] + * @return {Promise} + */ +export const apiGetAccountTransactions = async ( + address = '', + network = 'mainnet', + lastTxHash = '', + page = 1, +) => { + try { + // TODO: hit api directly instead of through indexer + let { data } = await apiGetTransactionData(address, network, page); + let { transactions, pages } = await parseAccountTransactions( + data, + address, + network, + ); + if (transactions.length && lastTxHash) { + const lastTxnHashIndex = findIndex(transactions, txn => { + return txn.hash === lastTxHash; + }); + if (lastTxnHashIndex > -1) { + transactions = slice(transactions, 0, lastTxnHashIndex); + pages = page; + } + } + transactions = await parseHistoricalTransactions(transactions, page); + const result = { data: transactions, pages }; + return result; + } catch (error) { + console.log('Error getting acct transactions', error); + throw error; + } +}; + +/** + * @desc get ethereum gas prices + * @return {Promise} + */ +export const apiGetGasPrices = () => api.get(`/get_eth_gas_prices`); diff --git a/src/handlers/commonStorage.js b/src/handlers/commonStorage.js new file mode 100644 index 00000000..535944fa --- /dev/null +++ b/src/handlers/commonStorage.js @@ -0,0 +1,272 @@ +import { differenceInMinutes } from 'date-fns'; +import { omit, pickBy } from 'lodash'; + +const defaultVersion = '0.1.0'; +const accountLocalVersion = '0.1.0'; +const globalSettingsVersion = '0.1.0'; +const walletConnectVersion = '0.1.0'; + +/** + * @desc save to storage + * @param {String} [key=''] + * @param {Object} [data={}] + * @param {String} [version=defaultVersion] + */ +export const saveLocal = async ( + key = '', + data = {}, + version = defaultVersion, +) => { + try { + data['storageVersion'] = version; + await window.storage.save({ key, data, expires: null }); + } catch (error) { + console.log('Storage: error saving to local for key', key); + } +}; + +/** + * @desc get from storage + * @param {String} [key=''] + * @return {Object} + */ +export const getLocal = async (key = '', version = defaultVersion) => { + try { + const result = await window.storage.load({ + key, + autoSync: false, + syncInBackground: false, + }); + if (result && result.storageVersion === version) { + return result; + } else if (result) { + removeLocal(key); + return null; + } + } catch (error) { + console.log('Storage: error getting from local for key', key); + return null; + } +}; + +/** + * @desc get from storage + * @param {String} [key=''] + * @return {Object} + */ +export const removeLocal = (key = '') => { + try { + window.storage.remove({ key }); + } catch (error) { + console.log('Storage: error removing local with key', key); + } +}; + +/** + * @desc reset account local + * @param {String} [address] + */ +export const resetAccount = accountAddress => { + const accountAddressKey = accountAddress.toLowerCase(); + removeLocal(accountAddressKey); + removeLocal('nativePrices'); +}; + +/** + * @desc get account local + * @param {String} [address] + * @return {Object} + */ +export const getAccountLocal = async accountAddress => { + return await getLocal(accountAddress.toLowerCase(), accountLocalVersion); +}; + +/** + * @desc get native prices + * @return {Object} + */ +export const getNativePrices = async () => { + const nativePrices = await getLocal('nativePrices', accountLocalVersion); + return nativePrices ? nativePrices.data : null; +}; + +/** + * @desc save native prices + * @param {String} [address] + */ +export const saveNativePrices = async nativePrices => { + await saveLocal('nativePrices', { data: nativePrices }, accountLocalVersion); +}; + +/** + * @desc get native currency + * @return {Object} + */ +export const getNativeCurrency = async () => { + const nativeCurrency = await getLocal( + 'nativeCurrency', + globalSettingsVersion, + ); + return nativeCurrency ? nativeCurrency.data : 'USD'; +}; + +/** + * @desc save native currency + * @param {String} [currency] + */ +export const saveNativeCurrency = async nativeCurrency => { + await saveLocal( + 'nativeCurrency', + { data: nativeCurrency }, + globalSettingsVersion, + ); +}; + +/** + * @desc update local balances + * @param {String} [address] + * @param {Object} [account] + * @param {String} [network] + * @return {Void} + */ +export const updateLocalBalances = async (address, account, network) => { + if (!address) return; + let accountLocal = await getAccountLocal(address); + if (!accountLocal) { + accountLocal = {}; + } + if (!accountLocal[network]) { + accountLocal[network] = {}; + } + accountLocal[network].type = account.type; + accountLocal[network].balances = { + assets: account.assets, + total: account.total || '———', + }; + await saveLocal(address.toLowerCase(), accountLocal, accountLocalVersion); +}; + +/** + * @desc update local unique tokens + * @param {String} [address] + * @param {Array} [unique tokens] + * @param {String} [network] + * @return {Void} + */ +export const updateLocalUniqueTokens = async ( + address, + uniqueTokens, + network, +) => { + if (!address) return; + let accountLocal = await getAccountLocal(address); + if (!accountLocal) { + accountLocal = {}; + } + if (!accountLocal[network]) { + accountLocal[network] = {}; + } + accountLocal[network].uniqueTokens = uniqueTokens; + await saveLocal(address.toLowerCase(), accountLocal, accountLocalVersion); +}; + +/** + * @desc update local transactions + * @param {String} [address] + * @param {Array} [transactions] + * @param {String} [network] + * @return {Void} + */ +export const updateLocalTransactions = async ( + address, + transactions, + network, +) => { + if (!address) return; + let accountLocal = await getAccountLocal(address); + if (!accountLocal) { + accountLocal = {}; + } + const pending = []; + const _transactions = []; + transactions.forEach(tx => { + if (tx.pending) { + pending.push(tx); + } else { + _transactions.push(tx); + } + }); + if (!accountLocal[network]) { + accountLocal[network] = {}; + } + accountLocal[network].transactions = _transactions; + accountLocal[network].pending = pending; + await saveLocal(address.toLowerCase(), accountLocal, accountLocalVersion); +}; + +/** + * @desc get all wallet connect sessions + * @return {Object} + */ +export const getAllValidWalletConnectSessions = async () => { + const allSessions = await getAllWalletConnectSessions(); + const validSessions = pickBy(allSessions, (value, key) => { + const expiration = new Date(value.expiration); + return new Date() < expiration; + }); + return validSessions; +}; + +/** + * @desc get all wallet connect sessions + * @return {Object} + */ +export const getAllWalletConnectSessions = async () => { + const allSessions = await getLocal('walletconnect', walletConnectVersion); + return allSessions ? allSessions : {}; +}; + +/** + * @desc save wallet connect session + * @param {String} [sessionId] + * @param {String} [uriString] + * @param {Number} [expirationDateInMs] + */ +export const saveWalletConnectSession = async ( + sessionId, + uriString, + expirationDateInMs, +) => { + let allSessions = await getAllValidWalletConnectSessions(); + allSessions[sessionId] = { uriString, expiration: expirationDateInMs }; + await saveLocal('walletconnect', allSessions, walletConnectVersion); +}; + +/** + * @desc remove wallet connect session + * @param {String} [sessionId] + */ +export const removeWalletConnectSession = async sessionId => { + const allSessions = await getAllWalletConnectSessions(); + const session = allSessions ? allSessions[sessionId] : null; + const resultingSessions = omit(allSessions, [sessionId]); + await saveLocal('walletconnect', resultingSessions, walletConnectVersion); + return session; +}; + +/** + * @desc get language + * @return {Object} + */ +export const getLanguage = async () => { + const language = await getLocal('language', globalSettingsVersion); + return language ? language.data : 'en'; +}; + +/** + * @desc save language + * @param {String} [language] + */ +export const saveLanguage = async language => { + await saveLocal('language', { data: language }, globalSettingsVersion); +}; diff --git a/src/handlers/ledger-eth.js b/src/handlers/ledger-eth.js index 709ba70b..f759ce41 100644 --- a/src/handlers/ledger-eth.js +++ b/src/handlers/ledger-eth.js @@ -2,7 +2,10 @@ import EthereumTx from 'ethereumjs-tx'; import TransportU2F from '@ledgerhq/hw-transport-u2f'; import AppEth from '@ledgerhq/hw-app-eth'; import ethereumNetworks from '../references/ethereum-networks.json'; -import { removeHexPrefix, getDerivationPathComponents } from 'balance-common'; +import { + removeHexPrefix, + getDerivationPathComponents, +} from '../helpers/utilities'; /** * @desc Ledger ETH App instance diff --git a/src/handlers/localstorage.js b/src/handlers/localstorage.js index a0b7f81b..fd3e5bd8 100644 --- a/src/handlers/localstorage.js +++ b/src/handlers/localstorage.js @@ -1,4 +1,4 @@ -import { commonStorage } from 'balance-common'; +import * as commonStorage from './commonStorage'; /** * @desc get suppress reminder ribbon setting diff --git a/src/handlers/opensea-api.js b/src/handlers/opensea-api.js new file mode 100644 index 00000000..bd1a74c5 --- /dev/null +++ b/src/handlers/opensea-api.js @@ -0,0 +1,30 @@ +import axios from 'axios'; +import { parseAccountUniqueTokens } from './parsers'; + +/** + * Configuration for opensea api + * @type axios instance + */ +const api = axios.create({ + baseURL: 'https://api.opensea.io/api/v1', + timeout: 30000, // 30 secs + headers: { + Accept: 'application/json', + 'X-API-KEY': process.env.REACT_APP_OPENSEA_API_KEY, + }, +}); + +/** + * @desc get opensea unique tokens + * @param {String} [address=''] + * @return {Promise} + */ +export const apiGetAccountUniqueTokens = async (address = '') => { + try { + const data = await api.get(`/assets?owner=${address}`); + return parseAccountUniqueTokens(data); + } catch (error) { + console.log('Error getting unique tokens', error); + throw error; + } +}; diff --git a/src/handlers/parsers.js b/src/handlers/parsers.js new file mode 100644 index 00000000..b452ac18 --- /dev/null +++ b/src/handlers/parsers.js @@ -0,0 +1,751 @@ +import lang from '../languages'; +import { get, pick } from 'lodash'; +import { + add, + convertAmountFromBigNumber, + convertAmountToBigNumber, + convertAmountToDisplay, + convertAmountToDisplaySpecific, + convertAmountToUnformattedDisplay, + convertAssetAmountToBigNumber, + convertAssetAmountToNativeAmount, + convertAssetAmountToNativeValue, + convertStringToNumber, + divide, + multiply, +} from '../helpers/bignumber'; +import ethUnits from '../references/ethereum-units.json'; +import nativeCurrencies from '../references/native-currencies.json'; +import timeUnits from '../references/time-units.json'; +import { debounceRequest } from '../helpers/utilities'; +import { getTransactionCount } from './web3'; +import { getTimeString } from '../helpers/time'; +import { apiGetHistoricalPrices } from './api'; + +/** + * @desc parse error code message + * @param {Error} error + * @return {String} + */ + +export const parseError = error => { + if (error) { + const msgEnd = + error.message.indexOf('\n') !== -1 + ? error.message.indexOf('\n') + : error.message.length; + let message = error.message.slice(0, msgEnd); + if ( + error.message.includes('MetaMask') || + error.message.includes('Returned error:') + ) { + message = message + .replace('Error: ', '') + .replace('MetaMask ', '') + .replace('Returned error: ', ''); + message = + message.slice(0, 1).toUpperCase() + message.slice(1).toLowerCase(); + + console.error(new Error(message)); + return message; + } else if (message.indexOf('0x6801') !== -1) { + message = lang.t('notification.error.generic_error'); + } + console.error(error); + return message; + } + return lang.t('notification.error.generic_error'); +}; +export const getTxFee = (gasPrice, gasLimit) => { + const amount = multiply(gasPrice, gasLimit); + return { + value: { + amount, + display: convertAmountToDisplay(amount, null, { + symbol: 'ETH', + decimals: 18, + }), + }, + native: null, + }; +}; + +export const defaultGasPriceFormat = ( + option, + timeAmount, + valueAmount, + valueDisplay, + short, +) => { + return { + option, + estimatedTime: { + amount: timeAmount, + display: getTimeString(timeAmount, 'ms', short), + }, + value: { + amount: valueAmount, + display: valueDisplay, + }, + }; +}; + +/** + * @desc parse ether gas prices + * @param {Object} data + * @param {Object} prices + * @param {Number} gasLimit + */ +export const parseGasPrices = (data, prices, gasLimit, short) => { + const gasPrices = { + slow: null, + average: null, + fast: null, + }; + if (!data) { + gasPrices.fast = defaultGasPriceFormat( + 'fast', + '30000', + '5000000000', + '5 Gwei', + short, + ); + gasPrices.average = defaultGasPriceFormat( + 'average', + '360000', + '2000000000', + '2 Gwei', + short, + ); + gasPrices.slow = defaultGasPriceFormat( + 'slow', + '1800000', + '1000000000', + '1 Gwei', + short, + ); + } else { + const fastTimeAmount = multiply(data.fastWait, timeUnits.ms.minute); + const fastValueAmount = divide(data.fast, 10); + gasPrices.fast = defaultGasPriceFormat( + 'fast', + fastTimeAmount, + multiply(fastValueAmount, ethUnits.gwei), + `${fastValueAmount} Gwei`, + short, + ); + + const avgTimeAmount = multiply(data.avgWait, timeUnits.ms.minute); + const avgValueAmount = divide(data.average, 10); + gasPrices.average = defaultGasPriceFormat( + 'average', + avgTimeAmount, + multiply(avgValueAmount, ethUnits.gwei), + `${avgValueAmount} Gwei`, + short, + ); + + const slowTimeAmount = multiply(data.safeLowWait, timeUnits.ms.minute); + const slowValueAmount = divide(data.safeLow, 10); + gasPrices.slow = defaultGasPriceFormat( + 'slow', + slowTimeAmount, + multiply(slowValueAmount, ethUnits.gwei), + `${slowValueAmount} Gwei`, + short, + ); + } + return parseGasPricesTxFee(gasPrices, prices, gasLimit); +}; + +export const convertGasPricesToNative = (prices, gasPrices) => { + const nativeGases = { ...gasPrices }; + if (prices && prices.selected) { + gasPrices.fast.txFee.native = getNativeGasPrice( + prices, + gasPrices.fast.txFee.value.amount, + ); + gasPrices.average.txFee.native = getNativeGasPrice( + prices, + gasPrices.average.txFee.value.amount, + ); + gasPrices.slow.txFee.native = getNativeGasPrice( + prices, + gasPrices.slow.txFee.value.amount, + ); + } + return nativeGases; +}; + +export const getNativeGasPrice = (prices, feeAmount) => { + const amount = convertAssetAmountToNativeAmount( + feeAmount, + { symbol: 'ETH' }, + prices, + ); + return { + selected: prices.selected, + value: { + amount, + display: convertAmountToDisplay(amount, prices, null, 2), + }, + }; +}; + +/** + * @desc parse ether gas prices with updated gas limit + * @param {Object} data + * @param {Object} prices + * @param {Number} gasLimit + */ +export const parseGasPricesTxFee = (gasPrices, prices, gasLimit) => { + gasPrices.fast.txFee = getTxFee(gasPrices.fast.value.amount, gasLimit); + gasPrices.average.txFee = getTxFee(gasPrices.average.value.amount, gasLimit); + gasPrices.slow.txFee = getTxFee(gasPrices.slow.value.amount, gasLimit); + return convertGasPricesToNative(prices, gasPrices); +}; + +/** + * @desc parse prices object from api response + * @param {Object} [data=null] + * @param {Array} [assets=[]] + * @param {String} [native='USD'] + * @return {Object} + */ +export const parsePricesObject = ( + data = null, + assets = [], + nativeSelected = 'USD', +) => { + let prices = { selected: nativeCurrencies[nativeSelected] }; + Object.keys(nativeCurrencies).forEach(nativeCurrency => { + prices[nativeCurrency] = {}; + assets.forEach(asset => { + let assetPrice = null; + if (data.RAW && data.RAW[asset]) { + assetPrice = { + price: { + amount: convertAmountToBigNumber( + data.RAW[asset][nativeCurrency].PRICE, + ), + display: convertAmountToDisplaySpecific( + convertAmountToBigNumber(data.RAW[asset][nativeCurrency].PRICE), + prices, + nativeCurrency, + ), + }, + change: { + amount: convertAmountToBigNumber( + data.RAW[asset][nativeCurrency].CHANGEPCT24HOUR, + ), + display: convertAmountToDisplay( + convertAmountToBigNumber( + data.RAW[asset][nativeCurrency].CHANGEPCT24HOUR, + ), + ), + }, + }; + } + if (asset !== 'WETH') { + prices[nativeCurrency][asset] = assetPrice; + } + if (asset === 'ETH') { + prices[nativeCurrency]['WETH'] = assetPrice; + } + }); + }); + return prices; +}; + +/** + * @desc parse account assets + * @param {Object} [data=null] + * @param {String} [address=null] + * @return {Object} + */ +export const parseAccountAssets = (data = null, address = '') => { + try { + let assets = [...data]; + assets = assets.map(assetData => { + const name = + assetData.contract.name !== assetData.contract.address + ? assetData.contract.name + : assetData.contract.symbol || 'Unknown Token'; + const asset = { + name: name, + symbol: assetData.contract.symbol || '———', + address: assetData.contract.address || null, + decimals: convertStringToNumber(assetData.contract.decimals), + }; + const assetBalance = convertAssetAmountToBigNumber( + assetData.balance, + asset.decimals, + ); + return { + ...asset, + balance: { + amount: assetBalance, + display: convertAmountToDisplay(assetBalance, null, { + symbol: asset.symbol, + decimals: asset.decimals, + }), + }, + native: null, + }; + }); + + assets = assets.filter( + asset => !!Number(asset.balance.amount) || asset.symbol === 'ETH', + ); + + return { + address: address, + type: '', + assets: assets, + total: null, + }; + } catch (error) { + throw error; + } +}; + +/** + * @desc parse account balances from native prices + * @param {Object} [account=null] + * @param {Object} [prices=null] + * @param {String} [network=''] + * @return {Object} + */ +export const parseAccountBalancesPrices = ( + account = null, + nativePrices = null, + network = '', +) => { + let totalAmount = 0; + let newAccount = { + ...account, + }; + // TODO: USD tracking selected + let nativeSelected = nativePrices.selected.currency; + //if (account && nativePrices && nativePrices.selected) { + if (account) { + const newAssets = account.assets.map(asset => { + if ( + !nativePrices || + (nativePrices && !nativePrices[nativeSelected][asset.symbol]) + ) + return asset; + + const balanceAmountUnit = convertAmountFromBigNumber( + asset.balance.amount, + asset.decimals, + ); + const balancePriceUnit = convertAmountFromBigNumber( + nativePrices[nativeSelected][asset.symbol].price.amount, + ); + const balanceRaw = multiply(balanceAmountUnit, balancePriceUnit); + const balanceAmount = convertAmountToBigNumber(balanceRaw); + const balanceDisplay = convertAmountToDisplay( + balanceAmount, + nativePrices, + ); + const assetPrice = nativePrices[nativeSelected][asset.symbol].price; + return { + ...asset, + native: { + selected: nativePrices.selected, + balance: { amount: balanceAmount, display: balanceDisplay }, + price: assetPrice, + change: + asset.symbol === nativePrices.selected.currency + ? { amount: '0', display: '———' } + : nativePrices[nativeSelected][asset.symbol].change, + }, + }; + }); + totalAmount = newAssets.reduce( + (total, asset) => + add(total, asset.native ? asset.native.balance.amount : 0), + 0, + ); + const totalDisplay = convertAmountToDisplay(totalAmount, nativePrices); + const totalTrackingAmount = convertAmountToUnformattedDisplay( + totalAmount, + nativePrices, + ); + const total = { + amount: totalAmount, + display: totalDisplay, + totalTrackingAmount, + }; + newAccount = { + ...newAccount, + assets: newAssets, + total: total, + }; + } + return newAccount; +}; + +/** + * @desc parse unique tokens from opensea + * @param {Object} + * @return {Array} + */ +export const parseAccountUniqueTokens = data => + get(data, 'data.assets', []).map( + ({ asset_contract, background_color, token_id, ...asset }) => ({ + ...pick(asset, [ + 'animation_url', + 'current_price', + 'description', + 'external_link', + 'image_original_url', + 'image_preview_url', + 'image_thumbnail_url', + 'image_url', + 'name', + 'permalink', + 'traits', + ]), + asset_contract: pick(asset_contract, [ + 'address', + 'description', + 'external_link', + 'featured_image_url', + 'hidden', + 'image_url', + 'name', + 'nft_version', + 'schema_name', + 'short_description', + 'symbol', + 'total_supply', + 'wiki_link', + ]), + background: background_color ? `#${background_color}` : null, + id: token_id, + lastPrice: asset.last_sale + ? Number(convertAmountFromBigNumber(asset.last_sale.total_price)) + : null, + }), + ); + +const ethFeeAsset = { + name: 'Ethereum', + symbol: 'ETH', + address: null, + decimals: 18, +}; + +/** + * @desc get historical native prices for transaction + * @param {Object} tx + * @return {Object} + */ +export const parseHistoricalNativePrice = async transaction => { + let tx = { ...transaction }; + // TODO: error: Date.now() provides ms not secs + const timestamp = tx.timestamp ? tx.timestamp.secs : Date.now(); + let asset = { ...tx.asset }; + asset.symbol = tx.asset.symbol === 'WETH' ? 'ETH' : tx.asset.symbol; + const priceAssets = [asset.symbol, 'ETH']; + const promises = priceAssets.map(x => apiGetHistoricalPrices(x, timestamp)); + const historicalPriceResponses = await Promise.all(promises); + const response = historicalPriceResponses[0]; + const feeResponse = historicalPriceResponses[1]; + + Object.keys(nativeCurrencies).forEach(nativeCurrency => { + let prices = { selected: nativeCurrencies[nativeCurrency] }; + prices[nativeCurrency] = {}; + if (response.data.response !== 'Error' && response.data[asset.symbol]) { + const assetPriceAmount = convertAmountToBigNumber( + response.data[asset.symbol][nativeCurrency], + ); + prices[nativeCurrency][asset.symbol] = { + price: { amount: assetPriceAmount, display: null }, + }; + const assetPriceDisplay = convertAmountToDisplay( + assetPriceAmount, + prices, + ); + prices[nativeCurrency][asset.symbol].price.display = assetPriceDisplay; + const assetPrice = prices[nativeCurrency][asset.symbol].price; + + const valuePriceAmount = convertAssetAmountToNativeValue( + tx.value.amount, + asset, + prices, + ); + const valuePriceDisplay = convertAmountToDisplay( + valuePriceAmount, + prices, + ); + const valuePrice = !tx.error + ? { amount: valuePriceAmount, display: valuePriceDisplay } + : { amount: '', display: '' }; + tx.native[nativeCurrency] = { + price: assetPrice, + value: valuePrice, + }; + } + + if ( + tx.txFee && + feeResponse.data.response !== 'Error' && + feeResponse.data['ETH'] + ) { + const feePriceAmount = convertAmountToBigNumber( + feeResponse.data['ETH'][nativeCurrency], + ); + prices[nativeCurrency]['ETH'] = { + price: { amount: feePriceAmount, display: null }, + }; + const feePriceDisplay = convertAmountToDisplay(feePriceAmount, prices); + prices[nativeCurrency]['ETH'].price.display = feePriceDisplay; + + const txFeePriceAmount = convertAssetAmountToNativeValue( + tx.txFee.amount, + ethFeeAsset, + prices, + ); + const txFeePriceDisplay = convertAmountToDisplay( + txFeePriceAmount, + prices, + ); + const txFeePrice = { + amount: txFeePriceAmount, + display: txFeePriceDisplay, + }; + tx.native[nativeCurrency] = { + ...tx.native[nativeCurrency], + txFee: txFeePrice, + }; + } + }); + + return tx; +}; + +/** + * @desc parse transactions from native prices + * @param {Object} [txDetails=null] + * @param {Object} [transactions=null] + * @param {Object} [nativeCurrency=''] + * @return {String} + */ +export const parseNewTransaction = async ( + txDetails = null, + nativeCurrency = '', +) => { + let totalGas = + txDetails.gasLimit && txDetails.gasPrice + ? multiply(txDetails.gasLimit, txDetails.gasPrice) + : null; + let txFee = totalGas + ? { + amount: totalGas, + display: convertAmountToDisplay(totalGas, null, { + symbol: 'ETH', + decimals: 18, + }), + } + : null; + + const amount = convertAmountToBigNumber( + txDetails.value, + txDetails.asset.decimals, + ); + const value = { + amount, + display: convertAmountToDisplay(amount, null, txDetails.asset), + }; + const nonce = + txDetails.nonce || + (txDetails.from ? await getTransactionCount(txDetails.from) : ''); + + let tx = { + hash: txDetails.hash, + timestamp: null, + from: txDetails.from, + to: txDetails.to, + error: false, + nonce: nonce, + value: value, + txFee: txFee, + native: { selected: nativeCurrencies[nativeCurrency] }, + pending: txDetails.hash ? true : false, + asset: txDetails.asset, + }; + + return await parseHistoricalNativePrice(tx); +}; + +/** + * @desc parse account transactions + * @param {Object} [data=null] + * @param {String} [address=''] + * @param {String} [networks=''] + * @return {Array} + */ +export const parseAccountTransactions = async ( + data = null, + address = '', + network = '', +) => { + if (!data || !data.docs) return { transactions: [], pages: 0 }; + + let transactions = await Promise.all( + data.docs.map(async tx => { + return await parseTransaction(tx); + }), + ); + let _transactions = []; + + transactions.forEach(tx => { + tx.forEach(subTx => { + _transactions.push(subTx); + }); + }); + + return { transactions: _transactions, pages: data.pages }; +}; + +/** + * @desc parse transaction + * @param {Object} [data=null] + * @return {Array} + */ +export const parseTransaction = async tx => { + const hash = tx._id; + const timestamp = { + secs: `${tx.timeStamp}`, + ms: `${tx.timeStamp}000`, + }; + const error = !!tx.error; + let from = tx.from; + let to = tx.to; + let asset = { + name: 'Ethereum', + symbol: 'ETH', + address: null, + decimals: 18, + }; + let value = { + amount: tx.value, + display: convertAmountToDisplay(tx.value, null, { + symbol: 'ETH', + decimals: 18, + }), + }; + let totalGas = multiply(tx.gasUsed, tx.gasPrice); + let txFee = { + amount: totalGas, + display: convertAmountToDisplay(totalGas, null, { + symbol: 'ETH', + decimals: 18, + }), + }; + + const includesTokenTransfer = (() => { + if (tx.input !== '0x' && tx.operations && tx.operations.length) { + const tokenTransfers = tx.operations.filter( + operation => operation.type === 'token_transfer', + ); + if (tokenTransfers.length) { + return true; + } + } + return false; + })(); + + let result = { + hash, + timestamp, + from, + to, + error, + value, + txFee, + native: {}, + pending: false, + asset, + }; + let results = [result]; + + if (includesTokenTransfer) { + const tokenTransfers = []; + if (tx.operations.length) { + tx.operations.forEach((transferData, idx) => { + const transferTx = { + hash: `${result.hash}-${idx + 1}`, + timestamp, + from, + to, + error, + value, + txFee, + native: {}, + pending: false, + asset, + }; + const contractEnabled = get(transferData, 'contract.enabled', false); + const contractName = get(transferData, 'contract.name', null); + const name = + contractEnabled && contractName && !contractName.startsWith('0x') + ? contractName + : get(transferData, 'contract.symbol', 'Unknown Token'); + transferTx.asset = { + name: name, + symbol: transferData.contract.symbol || '———', + address: transferData.contract.address || '', + decimals: transferData.contract.decimals || 18, + }; + + transferTx.from = transferData.from; + transferTx.to = transferData.to; + const amount = convertAssetAmountToBigNumber( + transferData.value, + transferTx.asset.decimals, + ); + transferTx.value = { + amount, + display: convertAmountToDisplay(amount, null, transferTx.asset), + }; + tokenTransfers.push(transferTx); + }); + if (!Number(tx.value)) { + results = [...tokenTransfers]; + } else { + result.hash = `${result.hash}-0`; + results = [...tokenTransfers, result]; + } + } + } + + return results; +}; + +/** + * @desc parse transaction historical prices + * @param {Array} [transactions=null] + * @return {Array} + */ +export const parseHistoricalTransactions = async (transactions, page) => { + if (!transactions.length) return transactions; + const pageOffset = (page - 1) * 2000; + const _transactions = await Promise.all( + transactions.map(async (tx, idx) => { + if (!tx.native || (tx.native && Object.keys(tx.native).length < 1)) { + const parsedTxn = await debounceRequest( + parseHistoricalNativePrice, + [tx], + 40 * idx + pageOffset, + ); + return parsedTxn; + } + return tx; + }), + ); + return _transactions; +}; diff --git a/src/handlers/web3.js b/src/handlers/web3.js index c3071991..a2176f25 100644 --- a/src/handlers/web3.js +++ b/src/handlers/web3.js @@ -2,6 +2,19 @@ import Web3 from 'web3'; import piwik from '../piwik'; import { ledgerEthSignTransaction } from './ledger-eth'; import { trezorEthSignTransaction } from './trezor-eth'; +import { isValidAddress } from '../helpers/validators'; +import { getDataString, removeHexPrefix } from '../helpers/utilities'; +import { + convertStringToNumber, + convertNumberToString, + convertAmountToBigNumber, + convertAssetAmountFromBigNumber, + convertHexToString, + convertStringToHex, + convertAmountToAssetAmount, +} from '../helpers/bignumber'; +import ethUnits from '../references/ethereum-units.json'; +import smartContractMethods from '../references/smartcontract-methods.json'; /** * @desc web3 http instance @@ -128,3 +141,242 @@ export const web3SendTransactionMultiWallet = ({ } return method(transaction); }; + +/** + * @desc set a different web3 provider + * @param {String} + */ +export const web3SetHttpProvider = provider => { + let providerObj = null; + if (provider.match(/(https?:\/\/)(\w+.)+/g)) { + providerObj = new Web3.providers.HttpProvider(provider); + } + if (!providerObj) { + throw new Error( + 'function web3SetHttpProvider requires provider to match a valid HTTP/HTTPS endpoint', + ); + } + return web3Instance.setProvider(providerObj); +}; + +/** + * @desc convert to checksum address + * @param {String} address + * @return {String} + */ +export const toChecksumAddress = address => { + if (typeof address === 'undefined') return ''; + + address = address.toLowerCase().replace('0x', ''); + const addressHash = web3Instance.utils.sha3(address).replace('0x', ''); + let checksumAddress = '0x'; + + for (let i = 0; i < address.length; i++) { + if (parseInt(addressHash[i], 16) > 7) { + checksumAddress += address[i].toUpperCase(); + } else { + checksumAddress += address[i]; + } + } + return checksumAddress; +}; + +/** + * @desc convert from wei to ether + * @param {Number} wei + * @return {BigNumber} + */ +export const fromWei = wei => web3Instance.utils.fromWei(wei); + +/** + * @desc convert from ether to wei + * @param {Number} ether + * @return {BigNumber} + */ +export const toWei = ether => web3Instance.utils.toWei(ether); + +/** + * @desc hash string with sha3 + * @param {String} string + * @return {String} + */ +export const sha3 = string => web3Instance.utils.sha3(string); + +/** + * @desc get address transaction count + * @param {String} address + * @return {Promise} + */ +export const getTransactionCount = address => + web3Instance.eth.getTransactionCount(address, 'pending'); + +/** + * @desc get transaction by hash + * @param {String} hash + * @return {Promise} + */ +export const getTransactionByHash = hash => + web3Instance.eth.getTransaction(hash); + +/** + * @desc get block by hash + * @param {String} hash + * @return {Promise} + */ +export const getBlockByHash = hash => web3Instance.eth.getBlock(hash); + +/** + * @desc get account ether balance + * @param {String} accountAddress + * @param {String} tokenAddress + * @return {Array} + */ +export const getAccountBalance = async address => { + const wei = await web3Instance.eth.getBalance(address); + const ether = fromWei(wei); + const balance = + convertStringToNumber(ether) !== 0 ? convertNumberToString(ether) : 0; + return balance; +}; + +/** + * @desc get account token balance + * @param {String} accountAddress + * @param {String} tokenAddress + * @return {Array} + */ +export const getTokenBalanceOf = (accountAddress, tokenAddress) => + new Promise((resolve, reject) => { + const balanceMethodHash = smartContractMethods.token_balance.hash; + const dataString = getDataString(balanceMethodHash, [ + removeHexPrefix(accountAddress), + ]); + web3Instance.eth + .call({ to: tokenAddress, data: dataString }) + .then(balanceHexResult => { + const balance = convertHexToString(balanceHexResult); + resolve(balance); + }) + .catch(error => reject(error)); + }); + +/** + * @desc get transaction details + * @param {Object} transaction { from, to, data, value, gasPrice, gasLimit } + * @return {Object} + */ +export const getTxDetails = async ({ + from, + to, + data, + value, + gasPrice, + gasLimit, +}) => { + const _gasPrice = gasPrice || (await web3Instance.eth.getGasPrice()); + const estimateGasData = value === '0x00' ? { from, to, data } : { to, data }; + const _gasLimit = + gasLimit || (await web3Instance.eth.estimateGas(estimateGasData)); + const nonce = await getTransactionCount(from); + const tx = { + data, + from, + gas: web3Instance.utils.toHex(_gasLimit), + gasLimit: web3Instance.utils.toHex(_gasLimit), + gasPrice: web3Instance.utils.toHex(_gasPrice), + nonce: web3Instance.utils.toHex(nonce), + to, + value: web3Instance.utils.toHex(value), + }; + return tx; +}; + +/** + * @desc get transfer token transaction + * @param {Object} transaction { asset, from, to, amount, gasPrice } + * @return {Object} + */ +export const getTransferTokenTransaction = transaction => { + const transferMethodHash = smartContractMethods.token_transfer.hash; + const value = convertStringToHex( + convertAmountToAssetAmount(transaction.amount, transaction.asset.decimals), + ); + const recipient = removeHexPrefix(transaction.to); + const dataString = getDataString(transferMethodHash, [recipient, value]); + return { + from: transaction.from, + to: transaction.asset.address, + data: dataString, + gasPrice: transaction.gasPrice, + gasLimit: transaction.gasLimit, + }; +}; + +/** + * @desc transform into signable transaction + * @param {Object} transaction { asset, from, to, amount, gasPrice } + * @return {Promise} + */ +export const createSignableTransaction = transaction => + new Promise((resolve, reject) => { + transaction.value = transaction.amount; + if (transaction.asset.symbol !== 'ETH') { + transaction = getTransferTokenTransaction(transaction); + } + const from = + transaction.from.substr(0, 2) === '0x' + ? transaction.from + : `0x${transaction.from}`; + const to = + transaction.to.substr(0, 2) === '0x' + ? transaction.to + : `0x${transaction.to}`; + const value = transaction.value ? toWei(transaction.value) : '0x00'; + const data = transaction.data ? transaction.data : '0x'; + getTxDetails({ + from, + to, + data, + value, + gasPrice: transaction.gasPrice, + gasLimit: transaction.gasLimit, + }) + .then(txDetails => resolve(txDetails)) + .catch(error => reject(error)); + }); + +/** + * @desc estimate gas limit + * @param {Object} [{selected, address, recipient, amount, gasPrice}] + * @return {String} + */ +export const estimateGasLimit = async ({ + asset, + address, + recipient, + amount, +}) => { + let gasLimit = ethUnits.basic_tx; + let data = '0x'; + let _amount = + amount && Number(amount) + ? convertAmountToBigNumber(amount) + : asset.balance.amount * 0.1; + let _recipient = + recipient && isValidAddress(recipient) + ? recipient + : '0x737e583620f4ac1842d4e354789ca0c5e0651fbb'; + let estimateGasData = { to: _recipient, data }; + if (asset.symbol !== 'ETH') { + const transferMethodHash = smartContractMethods.token_transfer.hash; + let value = convertAssetAmountFromBigNumber(_amount, asset.decimals); + value = convertStringToHex(value); + data = getDataString(transferMethodHash, [ + removeHexPrefix(_recipient), + value, + ]); + estimateGasData = { from: address, to: asset.address, data, value: '0x0' }; + gasLimit = await web3Instance.eth.estimateGas(estimateGasData); + } + return gasLimit; +}; diff --git a/src/helpers/bignumber.js b/src/helpers/bignumber.js new file mode 100644 index 00000000..f6fa3da4 --- /dev/null +++ b/src/helpers/bignumber.js @@ -0,0 +1,505 @@ +import BigNumber from 'bignumber.js'; +import ethUnits from '../references/ethereum-units.json'; +import nativeCurrencies from '../references/native-currencies.json'; + +export const fromWei = (number, decimals = 18) => { + return new BigNumber(number.toString(10), 10) + .dividedBy(BigNumber(10).pow(decimals)) + .toString(10); +}; + +/** + * @desc count value's number of decimals places + * @param {String} value + * @return {String} + */ +export const countDecimalPlaces = value => BigNumber(`${value}`).dp(); + +/** + * @desc convert from number to string + * @param {Number} value + * @return {String} + */ +export const convertNumberToString = value => BigNumber(`${value}`).toString(); + +/** + * @desc convert from string to number + * @param {String} value + * @return {Number} + */ +export const convertStringToNumber = value => BigNumber(`${value}`).toNumber(); + +/** + * @desc convert hex to number string + * @param {String} hex + * @return {String} + */ +export const convertHexToString = hex => BigNumber(`${hex}`).toString(); + +/** + * @desc convert number to string to hex + * @param {String} string + * @return {String} + */ +export const convertStringToHex = string => BigNumber(`${string}`).toString(16); + +/** + * @desc compares if numberOne is greater than numberTwo + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const greaterThan = (numberOne, numberTwo) => + BigNumber(`${numberOne}`).comparedTo(BigNumber(`${numberTwo}`)) === 1; + +/** + * @desc compares if numberOne is greater than or equal to numberTwo + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const greaterThanOrEqual = (numberOne, numberTwo) => + BigNumber(`${numberOne}`).comparedTo(BigNumber(`${numberTwo}`)) >= 0; + +/** + * @desc compares if numberOne is smaller than numberTwo + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const smallerThan = (numberOne, numberTwo) => + BigNumber(`${numberOne}`).comparedTo(BigNumber(`${numberTwo}`)) === -1; + +/** + * @desc compares if numberOne is smaller than or equal to numberTwo + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const smallerThanOrEqual = (numberOne, numberTwo) => + BigNumber(`${numberOne}`).comparedTo(BigNumber(`${numberTwo}`)) <= 0; + +/** + * @desc multiplies two numbers + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const multiply = (numberOne, numberTwo) => + BigNumber(`${numberOne}`) + .times(BigNumber(`${numberTwo}`)) + .toString(); + +/** + * @desc divides two numbers + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const divide = (numberOne, numberTwo) => + BigNumber(`${numberOne}`) + .dividedBy(BigNumber(`${numberTwo}`)) + .toString(); + +/** + * @desc real floor divides two numbers + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const floorDivide = (numberOne, numberTwo) => + BigNumber(`${numberOne}`) + .dividedToIntegerBy(BigNumber(`${numberTwo}`)) + .toString(); + +/** + * @desc modulos of two numbers + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const mod = (numberOne, numberTwo) => + BigNumber(`${numberOne}`) + .mod(BigNumber(`${numberTwo}`)) + .toString(); + +/** + * @desc adds two numbers + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const add = (numberOne, numberTwo) => + BigNumber(`${numberOne}`) + .plus(BigNumber(`${numberTwo}`)) + .toString(); + +/** + * @desc subtracts two numbers + * @param {Number} numberOne + * @param {Number} numberTwo + * @return {String} + */ +export const subtract = (numberOne, numberTwo) => + BigNumber(`${numberOne}`) + .minus(BigNumber(`${numberTwo}`)) + .toString(); + +/** + * @desc convert from amount value to BigNumber format + * @param {String|Number} value + * @return {BigNumber} + */ +export const convertAmountToBigNumber = value => + BigNumber(`${value}`) + .times(ethUnits.ether) + .toString(); + +/** + * @desc convert to amount value from BigNumber format + * @param {BigNumber} value + * @return {String} + */ +export const convertAmountFromBigNumber = value => + BigNumber(`${value}`) + .dividedBy(ethUnits.ether) + .toString(); + +/** + * @desc handle signficant decimals in display format + * @param {String} value + * @param {Number} decimals + * @param {Number} buffer + * @return {String} + */ +export const handleSignificantDecimals = (value, decimals, buffer) => { + const result = significantDecimals(value, decimals, buffer); + return BigNumber(`${result}`).dp() <= 2 + ? BigNumber(`${result}`).toFormat(2) + : BigNumber(`${result}`).toFormat(); +}; + +/** + * @desc handle signficant decimals + * @param {String} value + * @param {Number} decimals + * @param {Number} buffer + * @return {String} + */ +export const significantDecimals = (value, decimals, buffer) => { + if ( + !BigNumber(`${decimals}`).isInteger() || + (buffer && !BigNumber(`${buffer}`).isInteger()) + ) + return null; + buffer = buffer ? convertStringToNumber(buffer) : 3; + decimals = convertStringToNumber(decimals); + if (smallerThan(BigNumber(`${value}`).abs(), 1)) { + decimals = + value + .slice(2) + .slice('') + .search(/[^0]/g) + buffer; + decimals = decimals < 8 ? decimals : 8; + } else { + decimals = decimals < buffer ? decimals : buffer; + } + let result = BigNumber(`${value}`).toFixed(decimals); + result = BigNumber(`${result}`).toString(); + return result; +}; + +/** + * @desc convert from amount value to unformatted display + * @param {BigNumber} value + * @param {Object} nativePrices + * @param {Object} asset + * @param {Number} buffer + * @return {String} + */ +export const convertAmountToUnformattedDisplay = (value, nativePrices) => { + value = convertAmountFromBigNumber(value); + if (nativePrices) { + const decimals = nativePrices.selected.decimals; + return significantDecimals(value, decimals); + } + return value; +}; + +/** + * @desc convert from amount value to display formatted string + * @param {BigNumber} value + * @param {Object} nativePrices + * @param {Object} asset + * @param {Number} buffer + * @return {String} + */ +export const convertAmountToDisplay = (value, nativePrices, asset, buffer) => { + value = convertAmountFromBigNumber(value); + if (!nativePrices && !asset) { + const decimals = 2; + const display = handleSignificantDecimals(value, decimals, buffer); + return `${display}%`; + } else if (!nativePrices && asset) { + const decimals = asset.decimals || 18; + const display = handleSignificantDecimals(value, decimals, buffer); + return `${display} ${asset.symbol}`; + } else if (nativePrices) { + const decimals = nativePrices.selected.decimals; + const display = handleSignificantDecimals(value, decimals, buffer); + if (nativePrices.selected.alignment === 'left') { + return `${nativePrices.selected.symbol}${display}`; + } + return `${display} ${nativePrices.selected.currency}`; + } + return value; +}; + +/** + * @desc convert from amount value to display formatted string for specific currency + * @param {BigNumber} value + * @param {Object} nativePrices + * @param {Object} asset + * @return {String} + */ +export const convertAmountToDisplaySpecific = ( + value, + nativePrices, + selected, + buffer, +) => { + if (!nativePrices) return null; + value = convertAmountFromBigNumber(value); + const nativeSelected = nativeCurrencies[selected]; + const decimals = nativeSelected.decimals; + const display = handleSignificantDecimals(value, decimals, buffer); + if (nativeSelected.alignment === 'left') { + return `${nativeSelected.symbol}${display}`; + } + return `${display} ${nativeSelected.currency}`; +}; + +/** + * @desc convert from asset amount value to display formatted string for specific currency + * @param {BigNumber} value + * @param {Object} nativePrices + * @param {Object} asset + * @return {String} + */ +export const convertAssetAmountToDisplaySpecific = ( + value, + nativePrices, + selected, + buffer, +) => { + if (!nativePrices) return null; + const nativeSelected = nativeCurrencies[selected]; + const decimals = nativeSelected.decimals; + const display = handleSignificantDecimals(value, decimals, buffer); + if (nativeSelected.alignment === 'left') { + return `${nativeSelected.symbol}${display}`; + } + return `${display} ${nativeSelected.currency}`; +}; + +/** + * @desc convert from asset amount value to BigNumber format + * @param {String|Number} value + * @param {Number} decimals + * @return {BigNumber} + */ +export const convertAssetAmountToBigNumber = (value, decimals) => { + if (!BigNumber(`${decimals}`).isInteger()) return null; + decimals = convertStringToNumber(decimals); + value = BigNumber(`${value}`) + .dividedBy(BigNumber(10).pow(decimals)) + .toString(); + value = convertAmountToBigNumber(value); + return value; +}; + +/** + * @desc convert to asset amount value from BigNumber format + * @param {BigNumber} value + * @param {Number} decimals + * @return {String} + */ +export const convertAssetAmountFromBigNumber = (value, decimals) => { + if (!BigNumber(`${decimals}`).isInteger()) return null; + decimals = convertStringToNumber(decimals); + value = convertAmountFromBigNumber(value); + value = BigNumber(`${value}`) + .times(BigNumber(10).pow(decimals)) + .toString(); + return value; +}; + +/** + * @desc convert from asset amount units to native price value units + * @param {String} value + * @param {Object} asset + * @param {Object} nativePrices + * @return {String} + */ +export const convertAssetAmountToNativeValue = (value, asset, nativePrices) => { + const nativeSelected = nativePrices.selected.currency; + return convertAssetAmountToSpecifiedNativeValue( + value, + asset, + nativePrices, + nativeSelected, + ); +}; + +/** + * @desc convert from asset amount units to native price value units + * @param {String} value + * @param {Object} asset + * @param {Object} nativePrices + * @param {String} nativeSelected + * @return {String} + */ +export const convertAssetAmountToSpecifiedNativeValue = ( + value, + asset, + nativePrices, + nativeSelected = 'USD', +) => { + const assetPriceUnit = convertAmountFromBigNumber( + nativePrices[nativeSelected][asset.symbol].price.amount, + ); + const assetNativePrice = BigNumber(value) + .times(BigNumber(assetPriceUnit)) + .toString(); + return assetNativePrice; +}; + +/** + * @desc convert to asset amount units from native price value units + * @param {String} value + * @param {Object} asset + * @param {Object} nativePrices + * @return {String} + */ +export const convertAssetAmountFromNativeValue = ( + value, + asset, + nativePrices, +) => { + const nativeSelected = nativePrices.selected.currency; + const assetPriceUnit = convertAmountFromBigNumber( + nativePrices[nativeSelected][asset.symbol].price.amount, + ); + const assetAmountUnit = BigNumber(value) + .dividedBy(BigNumber(assetPriceUnit)) + .toString(); + return assetAmountUnit; +}; + +/** + * @desc convert from asset BigNumber amount to native price BigNumber amount + * @param {BigNumber} value + * @param {Object} asset + * @param {Object} nativePrices + * @return {BigNumber} + */ +export const convertAssetAmountToNativeAmount = ( + value, + asset, + nativePrices, +) => { + const nativeSelected = nativePrices.selected.currency; + const _value = convertAmountFromBigNumber(`${value}`); + const assetPriceUnit = convertAmountFromBigNumber( + nativePrices[nativeSelected][asset.symbol].price.amount, + ); + const assetNativePrice = BigNumber(_value) + .times(BigNumber(assetPriceUnit)) + .toString(); + return convertAmountToBigNumber(assetNativePrice); +}; + +/** + * @desc convert to asset BigNumber amount from native price BigNumber amount + * @param {BigNumber} value + * @param {Object} asset + * @param {Object} nativePrices + * @return {BigNumber} + */ +export const convertAssetAmountFromNativeAmount = ( + value, + asset, + nativePrices, +) => { + const nativeSelected = nativePrices.selected.currency; + const _value = convertAmountFromBigNumber(`${value}`); + const assetPriceUnit = convertAmountFromBigNumber( + nativePrices[nativeSelected][asset.symbol].price.amount, + ); + const assetAmountUnit = BigNumber(_value) + .dividedBy(BigNumber(assetPriceUnit)) + .toString(); + return convertAmountToBigNumber(assetAmountUnit); +}; + +/** + * @desc convert amount to asset amount + * @param {String} value + * @param {Number} decimals + * @return {String} + */ +export const convertAmountToAssetAmount = (value, decimals) => + BigNumber(value).times(BigNumber(10).pow(decimals)); + +/** + * @desc format fixed number of decimals + * @param {String} value + * @param {Number} decimals + * @return {String} + */ +export const formatFixedDecimals = (value, decimals) => { + const _value = convertNumberToString(value); + const _decimals = convertStringToNumber(decimals); + const result = BigNumber(BigNumber(_value).toFixed(_decimals)).toString(); + return result; +}; + +/** + * @desc format inputOne value to signficant decimals given inputTwo + * @param {String} inputOne + * @param {String} inputTwo + * @return {String} + */ +export const formatInputDecimals = (inputOne, inputTwo) => { + const _nativeAmountDecimalPlaces = countDecimalPlaces(inputTwo); + const decimals = + _nativeAmountDecimalPlaces > 8 ? _nativeAmountDecimalPlaces : 8; + const result = BigNumber(formatFixedDecimals(inputOne, decimals)) + .toFormat() + .replace(/,/g, ''); + return result; +}; + +/** + * @desc checks if asset has a high market value + * @param {Object} asset + * @return {Boolean} + */ +export const hasHighMarketValue = asset => + asset.native && + greaterThan( + convertAmountFromBigNumber(asset.native.balance.amount), + asset.native.selected.assetLimit, + ); + +/** + * @desc checks if asset has a low market value + * @param {Object} asset + * @return {Boolean} + */ +export const hasLowMarketValue = asset => + asset.native && + smallerThan( + convertAmountFromBigNumber(asset.native.balance.amount), + asset.native.selected.assetLimit, + ); diff --git a/src/helpers/time.js b/src/helpers/time.js new file mode 100644 index 00000000..937715dd --- /dev/null +++ b/src/helpers/time.js @@ -0,0 +1,195 @@ +import { + convertStringToNumber, + divide, + formatFixedDecimals, + floorDivide, + greaterThanOrEqual, + mod, + multiply, +} from './bignumber'; +import timeUnits from '../references/time-units.json'; +import lang from '../languages'; + +/** + * @desc get local time & date string + * @param {Number} [timestamp=null] + * @return {String} + */ +export const getLocalTimeDate = (timestamp = null) => { + timestamp = Number(timestamp) || Date.now(); + const date = new Date(timestamp); + return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`; +}; + +/** + * @desc get time string for minimal unit + * @param {String} [value=''] + * @param {String} [unit='ms'] + * @param {Boolean} [short=false] + * @return {String} + */ +export const getTimeString = (value = '', unit = 'ms', short = false) => { + if (!value) return null; + let _value = convertStringToNumber(value); + let _unit = ''; + let _unitShort = ''; + if (_value) { + if (unit === 'miliseconds' || unit === 'ms') { + if (_value === 1) { + _unit = lang.t('time.milisecond'); + _unitShort = lang.t('time.ms'); + } else if ( + _value >= timeUnits.ms.second && + _value < timeUnits.ms.minute + ) { + _value = formatFixedDecimals(divide(_value, timeUnits.ms.second), 2); + if (_value === 1) { + _unit = lang.t('time.second'); + _unitShort = lang.t('time.sec'); + } else { + _unit = lang.t('time.seconds'); + _unitShort = lang.t('time.secs'); + } + } else if (_value >= timeUnits.ms.minute && _value < timeUnits.ms.hour) { + _value = formatFixedDecimals(divide(_value, timeUnits.ms.minute), 2); + if (_value === 1) { + _unit = lang.t('time.minute'); + _unitShort = lang.t('time.min'); + } else { + _unit = lang.t('time.minutes'); + _unitShort = lang.t('time.mins'); + } + } else if (_value >= timeUnits.ms.hour && _value < timeUnits.ms.day) { + _value = formatFixedDecimals(divide(_value, timeUnits.ms.hour), 2); + if (_value === 1) { + _unit = lang.t('time.hour'); + _unitShort = lang.t('time.hr'); + } else { + _unit = lang.t('time.hours'); + _unitShort = lang.t('time.hrs'); + } + } else if (_value >= timeUnits.ms.day) { + _value = formatFixedDecimals(divide(_value, timeUnits.ms.day), 2); + if (_value === 1) { + _unit = lang.t('time.day'); + _unitShort = lang.t('time.day'); + } else { + _unit = lang.t('time.days'); + _unitShort = lang.t('time.days'); + } + } else { + _unit = lang.t('time.miliseconds'); + _unitShort = lang.t('time.ms'); + } + } else if (unit === 'seconds' || unit === 'secs') { + if (_value === 1) { + _unit = lang.t('time.second'); + _unitShort = lang.t('time.sec'); + } else if (_value < 1) { + _value = formatFixedDecimals(multiply(_value, timeUnits.ms.second)); + if (_value === 1) { + _unit = lang.t('time.milisecond'); + _unitShort = lang.t('time.ms'); + } else { + _unit = lang.t('time.miliseconds'); + _unitShort = lang.t('time.ms'); + } + } else if ( + _value >= timeUnits.secs.minute && + _value < timeUnits.secs.hour + ) { + _value = formatFixedDecimals(divide(_value, timeUnits.secs.minute), 2); + if (_value === 1) { + _unit = lang.t('time.minute'); + _unitShort = lang.t('time.min'); + } else { + _unit = lang.t('time.minutes'); + _unitShort = lang.t('time.mins'); + } + } else if (_value >= timeUnits.secs.hour && _value < timeUnits.secs.day) { + _value = formatFixedDecimals(divide(_value, timeUnits.secs.hour), 2); + if (_value === 1) { + _unit = lang.t('time.hour'); + _unitShort = lang.t('time.hr'); + } else { + _unit = lang.t('time.hours'); + _unitShort = lang.t('time.hrs'); + } + } else if (_value >= timeUnits.secs.day) { + _value = formatFixedDecimals(divide(_value, timeUnits.secs.day), 2); + if (_value === 1) { + _unit = lang.t('time.day'); + _unitShort = lang.t('time.day'); + } else { + _unit = lang.t('time.days'); + _unitShort = lang.t('time.days'); + } + } else { + _unit = lang.t('time.seconds'); + _unitShort = lang.t('time.secs'); + } + } else if (unit === 'minutes' || unit === 'mins') { + if (_value === 1) { + _unit = lang.t('time.minute'); + _unitShort = lang.t('time.min'); + } else if (_value < 1) { + _value = formatFixedDecimals(multiply(_value, timeUnits.secs.minute)); + if (_value === 1) { + _unit = lang.t('time.second'); + _unitShort = lang.t('time.sec'); + } else { + _unit = lang.t('time.seconds'); + _unitShort = lang.t('time.secs'); + } + } else if (_value > timeUnits.mins.hour && _value < timeUnits.mins.day) { + _value = formatFixedDecimals(divide(_value, timeUnits.mins.hour), 2); + if (_value === 1) { + _unit = lang.t('time.hour'); + _unitShort = lang.t('time.hr'); + } else { + _unit = lang.t('time.hours'); + _unitShort = lang.t('time.hrs'); + } + } else if (_value >= timeUnits.mins.day) { + _value = formatFixedDecimals(divide(_value, timeUnits.mins.day), 2); + if (_value === 1) { + _unit = lang.t('time.day'); + _unitShort = lang.t('time.day'); + } else { + _unit = lang.t('time.days'); + _unitShort = lang.t('time.days'); + } + } else { + _unit = lang.t('time.minutes'); + _unitShort = lang.t('time.mins'); + } + } + } + if (short) { + return `${_value} ${_unitShort}`; + } else { + return `${_value} ${_unit}`; + } +}; + +/** + * @desc get countdown (hrs:mins:secs) + * @param {Number} [miliseconds] + * @return {String} + */ +export const getCountdown = miliseconds => { + let remaining = miliseconds; + let slots = [timeUnits.ms.hour, timeUnits.ms.minute, timeUnits.ms.second]; + slots = slots.map(pack => { + let result = floorDivide(remaining, pack); + remaining = mod(remaining, pack); + if (greaterThanOrEqual(result, 1)) { + return result.length < 2 ? `0${result}` : result; + } else { + return null; + } + }); + return `${slots[0] ? `${slots[0]}:` : ''}${ + slots[1] ? `${slots[1]}:` : '00:' + }${slots[1] ? slots[2] || '00' : slots[2]}`; +}; diff --git a/src/helpers/utilities.js b/src/helpers/utilities.js new file mode 100644 index 00000000..993d9599 --- /dev/null +++ b/src/helpers/utilities.js @@ -0,0 +1,198 @@ +import { + convertAmountFromBigNumber, + convertNumberToString, + add, +} from './bignumber'; + +/** + * @desc debounce api request + * @param {Function} request + * @param {Array} params + * @param {Number} timeout + * @return {Promise} + */ +export const debounceRequest = (request, params, timeout) => + new Promise((resolve, reject) => + setTimeout( + () => + request(...params) + .then(res => { + resolve(res); + }) + .catch(err => reject(err)), + timeout, + ), + ); + +/** + * @desc capitalize string + * @param {String} [string] + * @return {String} + */ +export const capitalize = string => + string + .split(' ') + .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()) + .join(' '); + +/** + * @desc ellipse text to max maxLength + * @param {String} [text = ''] + * @param {Number} [maxLength = 9999] + * @return {Intercom} + */ +export const ellipseText = (text = '', maxLength = 9999) => { + if (text.length <= maxLength) return text; + const _maxLength = maxLength - 3; + let ellipse = false; + let currentLength = 0; + const result = + text + .split(' ') + .filter(word => { + currentLength += word.length; + if (ellipse || currentLength >= _maxLength) { + ellipse = true; + return false; + } else { + return true; + } + }) + .join(' ') + '...'; + return result; +}; + +/** + * @desc pad string to specific width and padding + * @param {String} n + * @param {Number} width + * @param {String} z + * @return {String} + */ +export const padLeft = (n, width, z) => { + z = z || '0'; + n = n + ''; + return n.length >= width ? n : new Array(width - n.length + 1).join(z) + n; +}; + +/** + * @desc sanitize hexadecimal string + * @param {String} address + * @return {String} + */ +export const sanitizeHex = hex => { + hex = hex.substring(0, 2) === '0x' ? hex.substring(2) : hex; + if (hex === '') return ''; + hex = hex.length % 2 !== 0 ? '0' + hex : hex; + return '0x' + hex; +}; + +/** + * @desc remove hex prefix + * @param {String} hex + * @return {String} + */ +export const removeHexPrefix = hex => hex.toLowerCase().replace('0x', ''); + +/** + * @desc get ethereum contract call data string + * @param {String} func + * @param {Array} arrVals + * @return {String} + */ +export const getDataString = (func, arrVals) => { + let val = ''; + for (let i = 0; i < arrVals.length; i++) val += padLeft(arrVals[i], 64); + const data = func + val; + return data; +}; + +/** + * @desc get derivation path components + * @param {String} [derivationPath = ''] + * @return {Object} + */ +export const getDerivationPathComponents = (derivationPath = '') => { + const regExp = /^(44'\/6[0|1]'\/\d+'?\/)(\d+)$/; + const matchResult = regExp.exec(derivationPath); + if (matchResult === null) { + throw new Error( + "To get multiple accounts your derivation path must follow pattern 44'/60|61'/x'/n ", + ); + } + return { basePath: matchResult[1], index: parseInt(matchResult[2], 10) }; +}; + +/** + * @desc returns url parameter value + * @param {String} parameter + * @param {String} url + * @return {String} + */ +export const getUrlParameter = ( + parameter, + url = typeof window !== 'undefined' ? window.location.href : '', +) => { + let name = parameter.replace(/[[]]/g, '\\$&'); + const regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'); + const results = regex.exec(url); + if (!results) return null; + if (!results[2]) return ''; + return decodeURIComponent(results[2].replace(/\+/g, ' ')); +}; + +/** + * @desc returns an eth asset object + * @param {Array} assets + * @return {Object} + */ +export const getEth = assets => { + return assets.filter(asset => asset.symbol === 'ETH')[0]; +}; + +/** + * @desc returns an object + * @param {String} accountInfo + * @param {String} assetAmount + * @param {String} gasPrice + * @return {Object} ethereum, balanceAmount, balance, requestedAmount, txFeeAmount, txFee, amountWithFees + */ +export const transactionData = (accountInfo, assetAmount, gasPrice) => { + const ethereum = getEth(accountInfo.assets); + const balanceAmount = ethereum.balance.amount; + const balance = convertAmountFromBigNumber(balanceAmount); + const requestedAmount = convertNumberToString(assetAmount); + const txFeeAmount = gasPrice.txFee.value.amount; + const txFee = convertAmountFromBigNumber(txFeeAmount); + const amountWithFees = add(requestedAmount, txFee); + + return { + ethereum, + balanceAmount, + balance, + requestedAmount, + txFeeAmount, + txFee, + amountWithFees, + }; +}; + +/** + * @desc calculates the native and tx fee for a transaction + * @param {Array} gasPrices + * @param {Object} gasPriceOption + * @param {Object} nativeCurrency + * @return {String} native and txFee + */ +export const calcTxFee = (gasPrices, gasPriceOption, nativeCurrency) => { + const option = gasPrices[gasPriceOption]; + const txFeeNative = + option && option.txFee.native ? option.txFee.native.value.display : '$0.00'; + const txFee = + nativeCurrency !== 'ETH' + ? ` (${ + option && option.txFee ? option.txFee.value.display : '0.000 ETH' + })` + : ''; + return `${txFeeNative}${txFee}`; +}; diff --git a/src/helpers/validators.js b/src/helpers/validators.js new file mode 100644 index 00000000..83aabfa3 --- /dev/null +++ b/src/helpers/validators.js @@ -0,0 +1,27 @@ +import { toChecksumAddress } from '../handlers/web3'; + +/** + * @desc validate email + * @param {string} email + * @return {Boolean} + */ +export const isValidEmail = email => + !!email.match( + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/, + ); + +/** + * @desc validate ethereum address + * @param {Number} wei + * @return {String} + */ +export const isValidAddress = address => { + if (address.substring(0, 2) !== '0x') return false; + else if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) return false; + else if ( + /^(0x)?[0-9a-f]{40}$/.test(address) || + /^(0x)?[0-9A-F]{40}$/.test(address) + ) + return true; + else return address === toChecksumAddress(address); +}; diff --git a/src/index.js b/src/index.js index 6f593f72..d309ffce 100644 --- a/src/index.js +++ b/src/index.js @@ -6,7 +6,7 @@ import { globalStyles } from './styles'; import { bootIntercom } from './helpers/bootIntercom'; import Root from './Root'; import Storage from '@devshack/react-native-storage'; -import { commonStorage } from 'balance-common'; +import * as commonStorage from './handlers/commonStorage'; import lang, { resources } from './languages'; const storage = new Storage({ diff --git a/src/layouts/base.js b/src/layouts/base.js index 15e90496..625eec76 100644 --- a/src/layouts/base.js +++ b/src/layouts/base.js @@ -23,12 +23,12 @@ import { accountClearState, accountUpdateAccountAddress, accountChangeLanguage, -} from 'balance-common'; +} from '../reducers/_account'; import { metamaskClearState } from '../reducers/_metamask'; import { ledgerClearState } from '../reducers/_ledger'; import { trezorClearState } from '../reducers/_trezor'; import { walletConnectClearState } from '../reducers/_walletconnect'; -import { commonStorage } from 'balance-common'; +import * as commonStorage from '../handlers/commonStorage'; import ReminderRibbon from '../components/ReminderRibbon'; import { colors } from '../styles'; diff --git a/src/modals/ApproveTransactionModal/index.js b/src/modals/ApproveTransactionModal/index.js index 61570a0d..be274005 100644 --- a/src/modals/ApproveTransactionModal/index.js +++ b/src/modals/ApproveTransactionModal/index.js @@ -1,6 +1,6 @@ import React from 'react'; -import { capitalize } from 'balance-common'; +import { capitalize } from '../../helpers/utilities'; import lang from '../../languages'; import MetamaskLogo from '../../components/MetamaskLogo'; diff --git a/src/modals/DonateModal/index.js b/src/modals/DonateModal/index.js index 7694e641..964cf4c0 100644 --- a/src/modals/DonateModal/index.js +++ b/src/modals/DonateModal/index.js @@ -20,13 +20,8 @@ import { modalClose } from '../../reducers/_modal'; import { web3SendTransactionMultiWallet } from '../../handlers/web3'; import { notificationShow } from '../../reducers/_notification'; import lang from '../../languages'; - -import { - capitalize, - getEth, - calcTxFee, - withSendComponentWithData, -} from 'balance-common'; +import { capitalize, getEth, calcTxFee } from '../../helpers/utilities'; +import { withSendComponentWithData } from '../../components/SendComponentWithData'; import { StyledIcon, diff --git a/src/modals/ExchangeModal/index.js b/src/modals/ExchangeModal/index.js index 564f67b6..6090f532 100644 --- a/src/modals/ExchangeModal/index.js +++ b/src/modals/ExchangeModal/index.js @@ -29,20 +29,20 @@ import { exchangeMaxBalance, } from '../../reducers/_exchange'; import { notificationShow } from '../../reducers/_notification'; +import { capitalize } from '../../helpers/utilities'; import { add, - capitalize, convertAmountFromBigNumber, convertAmountToDisplay, convertNumberToString, - getCountdown, multiply, divide, greaterThan, smallerThan, convertAmountToBigNumber, handleSignificantDecimals, -} from 'balance-common'; +} from '../../helpers/bignumber'; +import { getCountdown } from '../../helpers/time'; import { fonts, colors, responsive, transitions } from '../../styles'; import { diff --git a/src/modals/ReceiveModal/index.js b/src/modals/ReceiveModal/index.js index dfe20e61..e9bb2a47 100644 --- a/src/modals/ReceiveModal/index.js +++ b/src/modals/ReceiveModal/index.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import styled from 'styled-components'; -import { capitalize } from 'balance-common'; +import { capitalize } from '../../helpers/utilities'; import lang from '../../languages'; import Card from '../../components/Card'; diff --git a/src/modals/SendModal/index.js b/src/modals/SendModal/index.js index 0d7d6b39..291dcbf8 100644 --- a/src/modals/SendModal/index.js +++ b/src/modals/SendModal/index.js @@ -20,12 +20,9 @@ import qrIcon from '../../assets/qr-code-bnw.png'; import { modalClose } from '../../reducers/_modal'; import lang from '../../languages'; -import { - calcTxFee, - capitalize, - withSendComponentWithData, -} from 'balance-common'; import { web3SendTransactionMultiWallet } from '../../handlers/web3'; +import { capitalize, calcTxFee } from '../../helpers/utilities'; +import { withSendComponentWithData } from '../../components/SendComponentWithData'; import { StyledIcon, diff --git a/src/modals/index.js b/src/modals/index.js index 38d2c795..24bc0ada 100644 --- a/src/modals/index.js +++ b/src/modals/index.js @@ -11,7 +11,7 @@ import ReceiveModal from './ReceiveModal'; import DonateModal from './DonateModal'; import WalletConnectModal from './WalletConnectModal'; import { modalClose } from '../reducers/_modal'; -import { sendClearFields } from 'balance-common'; +import { sendClearFields } from '../reducers/_send'; import { exchangeClearFields } from '../reducers/_exchange'; import { colors, transitions } from '../styles'; diff --git a/src/pages/index.js b/src/pages/index.js index 769851a4..72a22168 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import styled from 'styled-components'; import { connect } from 'react-redux'; import lang from '../languages'; -import { accountInitializeState } from 'balance-common'; +import { accountInitializeState } from '../reducers/_account'; import Link from '../components/Link'; import BaseLayout from '../layouts/base'; import Card from '../components/Card'; diff --git a/src/reducers/_account.js b/src/reducers/_account.js new file mode 100644 index 00000000..cdf940fd --- /dev/null +++ b/src/reducers/_account.js @@ -0,0 +1,727 @@ +import _ from 'lodash'; +import lang, { updateLanguage } from '../languages'; +import { + apiGetAccountBalances, + apiGetAccountTransactions, + apiGetPrices, +} from '../handlers/api'; +import { apiGetAccountUniqueTokens } from '../handlers/opensea-api.js'; +import { + parseError, + parseAccountBalancesPrices, + parseNewTransaction, + parsePricesObject, +} from '../handlers/parsers'; +import { + getAccountLocal, + getLanguage, + getNativePrices, + getNativeCurrency, + resetAccount, + saveLanguage, + saveNativeCurrency, + saveNativePrices, + updateLocalBalances, + updateLocalTransactions, + updateLocalUniqueTokens, +} from '../handlers/commonStorage'; +import { web3SetHttpProvider } from '../handlers/web3'; +import { notificationShow } from './_notification'; +import nativeCurrencies from '../references/native-currencies.json'; + +// -- Constants ------------------------------------------------------------- // +const ACCOUNT_GET_ACCOUNT_TRANSACTIONS_REQUEST = + 'account/ACCOUNT_GET_ACCOUNT_TRANSACTIONS_REQUEST'; +const ACCOUNT_GET_ACCOUNT_TRANSACTIONS_SUCCESS = + 'account/ACCOUNT_GET_ACCOUNT_TRANSACTIONS_SUCCESS'; +const ACCOUNT_GET_ACCOUNT_TRANSACTIONS_NO_NEW_PAYLOAD_SUCCESS = + 'account/ACCOUNT_GET_ACCOUNT_TRANSACTIONS_NO_NEW_PAYLOAD_SUCCESS'; +const ACCOUNT_GET_ACCOUNT_TRANSACTIONS_FAILURE = + 'account/ACCOUNT_GET_ACCOUNT_TRANSACTIONS_FAILURE'; + +const ACCOUNT_CHECK_TRANSACTION_STATUS_REQUEST = + 'account/ACCOUNT_CHECK_TRANSACTION_STATUS_REQUEST'; +const ACCOUNT_CHECK_TRANSACTION_STATUS_SUCCESS = + 'account/ACCOUNT_CHECK_TRANSACTION_STATUS_SUCCESS'; +const ACCOUNT_CHECK_TRANSACTION_STATUS_FAILURE = + 'account/ACCOUNT_CHECK_TRANSACTION_STATUS_FAILURE'; + +const ACCOUNT_UPDATE_TRANSACTIONS_REQUEST = + 'account/ACCOUNT_UPDATE_TRANSACTIONS_REQUEST'; +const ACCOUNT_UPDATE_TRANSACTIONS_SUCCESS = + 'account/ACCOUNT_UPDATE_TRANSACTIONS_SUCCESS'; +const ACCOUNT_UPDATE_TRANSACTIONS_FAILURE = + 'account/ACCOUNT_UPDATE_TRANSACTIONS_FAILURE'; + +const ACCOUNT_GET_ACCOUNT_BALANCES_REQUEST = + 'account/ACCOUNT_GET_ACCOUNT_BALANCES_REQUEST'; +const ACCOUNT_GET_ACCOUNT_BALANCES_SUCCESS = + 'account/ACCOUNT_GET_ACCOUNT_BALANCES_SUCCESS'; +const ACCOUNT_GET_ACCOUNT_BALANCES_FAILURE = + 'account/ACCOUNT_GET_ACCOUNT_BALANCES_FAILURE'; + +const ACCOUNT_UPDATE_BALANCES_REQUEST = + 'account/ACCOUNT_UPDATE_BALANCES_REQUEST'; +const ACCOUNT_UPDATE_BALANCES_SUCCESS = + 'account/ACCOUNT_UPDATE_BALANCES_SUCCESS'; +const ACCOUNT_UPDATE_BALANCES_FAILURE = + 'account/ACCOUNT_UPDATE_BALANCES_FAILURE'; + +const ACCOUNT_GET_NATIVE_PRICES_REQUEST = + 'account/ACCOUNT_GET_NATIVE_PRICES_REQUEST'; +const ACCOUNT_GET_NATIVE_PRICES_SUCCESS = + 'account/ACCOUNT_GET_NATIVE_PRICES_SUCCESS'; +const ACCOUNT_GET_NATIVE_PRICES_FAILURE = + 'account/ACCOUNT_GET_NATIVE_PRICES_FAILURE'; + +const ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_REQUEST = + 'account/ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_REQUEST'; +const ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_SUCCESS = + 'account/ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_SUCCESS'; +const ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_FAILURE = + 'account/ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_FAILURE'; + +const ACCOUNT_INITIALIZE_PRICES_SUCCESS = + 'account/ACCOUNT_INITIALIZE_PRICES_SUCCESS'; +const ACCOUNT_INITIALIZE_PRICES_FAILURE = + 'account/ACCOUNT_INITIALIZE_PRICES_FAILURE'; + +const ACCOUNT_UPDATE_NETWORK = 'account/ACCOUNT_UPDATE_NETWORK'; +const ACCOUNT_UPDATE_ACCOUNT_ADDRESS = 'account/ACCOUNT_UPDATE_ACCOUNT_ADDRESS'; +const ACCOUNT_UPDATE_HAS_PENDING_TRANSACTION = + 'account/ACCOUNT_UPDATE_HAS_PENDING_TRANSACTION'; +const ACCOUNT_CLEAR_STATE = 'account/ACCOUNT_CLEAR_STATE'; + +const ACCOUNT_CHANGE_NATIVE_CURRENCY_SUCCESS = + 'account/ACCOUNT_CHANGE_NATIVE_CURRENCY_SUCCESS'; +const ACCOUNT_CHANGE_NATIVE_CURRENCY_FAILURE = + 'account/ACCOUNT_CHANGE_NATIVE_CURRENCY_FAILURE'; + +const ACCOUNT_CHANGE_LANGUAGE_SUCCESS = + 'account/ACCOUNT_CHANGE_LANGUAGE_SUCCESS'; +const ACCOUNT_CHANGE_LANGUAGE_FAILURE = + 'account/ACCOUNT_CHANGE_LANGUAGE_FAILURE'; + +// -- Actions --------------------------------------------------------------- // +let getAccountTransactionsInterval = null; +let getAccountBalancesInterval = null; + +export const accountUpdateHasPendingTransaction = ( + hasPending = true, +) => dispatch => { + dispatch({ + type: ACCOUNT_UPDATE_HAS_PENDING_TRANSACTION, + payload: hasPending, + }); +}; + +export const accountInitializeState = () => dispatch => { + getLanguage() + .then(language => { + dispatch({ + type: ACCOUNT_CHANGE_LANGUAGE_SUCCESS, + payload: { language }, + }); + }) + .catch(error => { + dispatch({ + type: ACCOUNT_CHANGE_LANGUAGE_FAILURE, + }); + }); + getNativeCurrency() + .then(nativeCurrency => { + dispatch({ + type: ACCOUNT_INITIALIZE_PRICES_SUCCESS, + payload: { nativeCurrency }, + }); + }) + .catch(error => { + dispatch({ + type: ACCOUNT_INITIALIZE_PRICES_FAILURE, + }); + }); +}; + +export const accountUpdateTransactions = txDetails => (dispatch, getState) => + new Promise((resolve, reject) => { + dispatch({ type: ACCOUNT_UPDATE_TRANSACTIONS_REQUEST }); + const currentTransactions = getState().account.transactions; + const network = getState().account.network; + const address = getState().account.accountInfo.address; + const nativeCurrency = getState().account.nativeCurrency; + parseNewTransaction(txDetails, nativeCurrency) + .then(parsedTransaction => { + let _transactions = [...currentTransactions]; + // TODO: could technically send in pending and normal separately + _transactions = [parsedTransaction, ..._transactions]; + updateLocalTransactions(address, _transactions, network); + dispatch({ + type: ACCOUNT_UPDATE_TRANSACTIONS_SUCCESS, + payload: _transactions, + }); + resolve(true); + }) + .catch(error => { + dispatch({ type: ACCOUNT_UPDATE_TRANSACTIONS_FAILURE }); + const message = parseError(error); + dispatch(notificationShow(message, true)); + reject(false); + }); + }); + +export const accountUpdateAccountAddress = (accountAddress, accountType) => ( + dispatch, + getState, +) => { + if (!accountAddress || !accountType) return; + const { network } = getState().account; + if (getState().account.accountType !== accountType) + dispatch(accountClearState()); + if ( + getState().account.accountType === accountType && + getState().account.accountAddress !== accountAddress + ) { + resetAccount(getState().account.accountAddress); + dispatch(accountClearState()); + } + dispatch({ + type: ACCOUNT_UPDATE_ACCOUNT_ADDRESS, + payload: { accountAddress, accountType }, + }); + dispatch(accountUpdateNetwork(network)); + dispatch(accountGetAccountTransactions()); + dispatch(accountGetAccountBalances()); + dispatch(accountGetUniqueTokens()); +}; + +export const accountUpdateNetwork = network => dispatch => { + web3SetHttpProvider(`https://${network}.infura.io/`); + dispatch({ type: ACCOUNT_UPDATE_NETWORK, payload: network }); +}; + +export const accountChangeLanguage = language => dispatch => { + //TODO: needs to trigger render after change + updateLanguage(language); + saveLanguage(language) + .then(() => { + dispatch({ + type: ACCOUNT_CHANGE_LANGUAGE_SUCCESS, + payload: { language }, + }); + }) + .catch(error => { + dispatch({ + type: ACCOUNT_CHANGE_LANGUAGE_FAILURE, + }); + }); +}; + +export const accountChangeNativeCurrency = nativeCurrency => ( + dispatch, + getState, +) => { + const prices = getState().account.prices; + if (prices) { + dispatch(accountUpdatePrices(nativeCurrency, prices)); + } else { + getNativePrices() + .then(nativePrices => { + dispatch(accountUpdatePrices(nativeCurrency, nativePrices)); + }) + .catch(error => { + dispatch({ + type: ACCOUNT_CHANGE_NATIVE_CURRENCY_FAILURE, + }); + }); + } +}; + +const accountUpdatePrices = (nativeCurrency, prices) => ( + dispatch, + getState, +) => { + saveNativeCurrency(nativeCurrency); + const accountAddress = getState().account.accountAddress; + const network = getState().account.network; + const selected = nativeCurrencies[nativeCurrency]; + let oldAccountInfo = getState().account.accountInfo; + let newPrices = { ...prices, selected }; + const newAccountInfo = parseAccountBalancesPrices(oldAccountInfo, newPrices); + const accountInfo = { ...oldAccountInfo, ...newAccountInfo }; + updateLocalBalances(accountAddress, accountInfo, network); + dispatch({ + type: ACCOUNT_CHANGE_NATIVE_CURRENCY_SUCCESS, + payload: { nativeCurrency, prices: newPrices, accountInfo }, + }); +}; + +export const accountClearState = () => dispatch => { + clearInterval(getAccountBalancesInterval); + clearInterval(getAccountTransactionsInterval); + dispatch({ type: ACCOUNT_CLEAR_STATE }); +}; + +const accountGetNativePrices = accountInfo => (dispatch, getState) => { + const assetSymbols = accountInfo.assets.map(asset => asset.symbol); + dispatch({ + type: ACCOUNT_GET_NATIVE_PRICES_REQUEST, + payload: getState().account.nativeCurrency, + }); + apiGetPrices(assetSymbols) + .then(({ data }) => { + const nativePriceRequest = getState().account.nativePriceRequest; + const nativeCurrency = getState().account.nativeCurrency; + const network = getState().account.network; + if (nativeCurrency === nativePriceRequest) { + const prices = parsePricesObject(data, assetSymbols, nativeCurrency); + const parsedAccountInfo = parseAccountBalancesPrices( + accountInfo, + prices, + network, + ); + updateLocalBalances( + parsedAccountInfo.address, + parsedAccountInfo, + network, + ); + saveNativePrices(prices); + dispatch({ + type: ACCOUNT_GET_NATIVE_PRICES_SUCCESS, + payload: { accountInfo: parsedAccountInfo, prices }, + }); + } + }) + .catch(error => { + dispatch({ type: ACCOUNT_GET_NATIVE_PRICES_FAILURE }); + const message = parseError(error); + dispatch(notificationShow(message, true)); + }); +}; + +const accountGetAccountBalances = () => (dispatch, getState) => { + const { + network, + accountInfo, + accountAddress, + accountType, + } = getState().account; + let cachedAccount = { ...accountInfo }; + if (accountInfo.address && accountInfo.accountType) { + dispatch(accountUpdateBalances()); + } else { + getAccountLocal(accountAddress) + .then(accountLocal => { + if (accountLocal && accountLocal[network]) { + if (accountLocal[network].balances) { + cachedAccount = { + ...cachedAccount, + type: accountType, + assets: accountLocal[network].balances.assets, + total: accountLocal[network].balances.total, + }; + } + if (accountLocal[network].type && !cachedAccount.type) { + cachedAccount.type = accountLocal[network].type; + } + dispatch({ + type: ACCOUNT_GET_ACCOUNT_BALANCES_REQUEST, + payload: { + accountType: cachedAccount.type || accountType, + accountInfo: cachedAccount, + fetching: + (accountLocal && !accountLocal[network]) || !accountLocal, + }, + }); + } + dispatch(accountUpdateBalances()); + }) + .catch(error => { + const message = parseError(error); + dispatch(notificationShow(message, true)); + }); + } +}; + +const accountUpdateBalances = () => (dispatch, getState) => { + const { network, accountAddress, accountType } = getState().account; + dispatch({ type: ACCOUNT_UPDATE_BALANCES_REQUEST }); + const getAccountBalances = () => { + apiGetAccountBalances(accountAddress, network) + .then(({ data }) => { + let accountInfo = { ...data, type: accountType }; + dispatch({ + type: ACCOUNT_GET_ACCOUNT_BALANCES_SUCCESS, + }); + dispatch(accountGetNativePrices(accountInfo)); + }) + .catch(error => { + const message = parseError(error); + dispatch(notificationShow(message, true)); + dispatch({ type: ACCOUNT_UPDATE_BALANCES_FAILURE }); + }); + }; + getAccountBalances(); + clearInterval(getAccountBalancesInterval); + getAccountBalancesInterval = setInterval(getAccountBalances, 15000); // 15 secs +}; + +const accountGetTransactions = (accountAddress, network, lastTxHash, page) => ( + dispatch, + getState, +) => { + const existingTransactions = getState().account.transactions; + const partitions = _.partition(existingTransactions, txn => txn.pending); + dispatch( + accountGetTransactionsPages({ + newTransactions: [], + pendingTransactions: partitions[0], + confirmedTransactions: partitions[1], + accountAddress, + network, + lastTxHash, + page, + }), + ); +}; + +const accountGetTransactionsPages = ({ + newTransactions, + pendingTransactions, + confirmedTransactions, + accountAddress, + network, + lastTxHash, + page, +}) => (dispatch, getState) => { + apiGetAccountTransactions(accountAddress, network, lastTxHash, page) + .then(({ data: transactionsForPage, pages }) => { + if (!transactionsForPage.length) { + dispatch({ + type: ACCOUNT_GET_ACCOUNT_TRANSACTIONS_NO_NEW_PAYLOAD_SUCCESS, + }); + return; + } + let updatedPendingTransactions = pendingTransactions; + if (pendingTransactions.length) { + updatedPendingTransactions = _.filter( + pendingTransactions, + pendingTxn => { + const matchingElement = _.find( + transactionsForPage, + txn => txn.hash && txn.hash.startsWith(pendingTxn.hash), + ); + return !matchingElement; + }, + ); + } + const address = getState().account.accountAddress; + let _newPages = newTransactions.concat(transactionsForPage); + let _transactions = _.unionBy( + updatedPendingTransactions, + _newPages, + confirmedTransactions, + 'hash', + ); + updateLocalTransactions(address, _transactions, network); + dispatch({ + type: ACCOUNT_GET_ACCOUNT_TRANSACTIONS_SUCCESS, + payload: _transactions, + }); + if (page < pages) { + const nextPage = page + 1; + dispatch( + accountGetTransactionsPages({ + newTransactions: _newPages, + pendingTransactions: updatedPendingTransactions, + confirmedTransactions, + accountAddress, + network, + lastTxHash, + page: nextPage, + }), + ); + } + }) + .catch(error => { + dispatch( + notificationShow( + lang.t('notification.error.failed_get_account_tx'), + true, + ), + ); + dispatch({ type: ACCOUNT_GET_ACCOUNT_TRANSACTIONS_FAILURE }); + }); +}; + +const accountGetAccountTransactions = () => (dispatch, getState) => { + const getAccountTransactions = () => { + const { accountAddress, network, transactions } = getState().account; + if (transactions.length) { + const lastSuccessfulTxn = _.find( + transactions, + txn => txn.hash && !txn.pending, + ); + const lastTxHash = lastSuccessfulTxn ? lastSuccessfulTxn.hash : ''; + dispatch(accountGetTransactions(accountAddress, network, lastTxHash, 1)); + } else { + let cachedTransactions = []; + let confirmedTransactions = []; + getAccountLocal(accountAddress) + .then(accountLocal => { + if (accountLocal && accountLocal[network]) { + if (accountLocal[network].pending) { + cachedTransactions = [...accountLocal[network].pending]; + } + if (accountLocal[network].transactions) { + confirmedTransactions = accountLocal[network].transactions; + cachedTransactions = _.unionBy( + cachedTransactions, + accountLocal[network].transactions, + 'hash', + ); + } + } + dispatch({ + type: ACCOUNT_GET_ACCOUNT_TRANSACTIONS_REQUEST, + payload: { + transactions: cachedTransactions, + fetchingTransactions: + (accountLocal && !accountLocal[network]) || + !accountLocal || + !accountLocal[network].transactions || + !accountLocal[network].transactions.length, + }, + }); + const lastSuccessfulTxn = _.find( + confirmedTransactions, + txn => txn.hash, + ); + const lastTxHash = lastSuccessfulTxn ? lastSuccessfulTxn.hash : ''; + dispatch( + accountGetTransactions(accountAddress, network, lastTxHash, 1), + ); + }) + .catch(error => { + console.log('error', error); + dispatch({ type: ACCOUNT_GET_ACCOUNT_TRANSACTIONS_FAILURE }); + }); + } + }; + getAccountTransactions(); + clearInterval(getAccountTransactionsInterval); + getAccountTransactionsInterval = setInterval(getAccountTransactions, 15000); // 15 secs +}; + +const accountGetUniqueTokens = () => (dispatch, getState) => { + dispatch({ type: ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_REQUEST }); + const { accountAddress, network } = getState().account; + getAccountLocal(accountAddress) + .then(accountLocal => { + const cachedUniqueTokens = _.get( + accountLocal, + `${network}.uniqueTokens`, + null, + ); + if (cachedUniqueTokens) { + updateLocalUniqueTokens(accountAddress, cachedUniqueTokens, network); + dispatch({ + type: ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_SUCCESS, + payload: cachedUniqueTokens, + }); + } + apiGetAccountUniqueTokens(accountAddress) + .then(data => { + updateLocalUniqueTokens(accountAddress, data, network); + dispatch({ + type: ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_SUCCESS, + payload: data, + }); + }) + .catch(error => { + const message = parseError(error); + dispatch(notificationShow(message, true)); + dispatch({ type: ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_FAILURE }); + }); + }) + .catch(error => { + dispatch({ type: ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_FAILURE }); + }); +}; + +// -- Reducer --------------------------------------------------------------- // +export const INITIAL_ACCOUNT_STATE = { + accountType: '', + accountAddress: '', + accountInfo: { + address: '', + accountType: '', + assets: [ + { + name: 'Ethereum', + symbol: 'ETH', + address: null, + decimals: 18, + balance: { + amount: '', + display: '0.00 ETH', + }, + native: null, + }, + ], + total: null, + }, + fetching: null, + fetchingTransactions: false, + fetchingUniqueTokens: false, + hasPendingTransaction: false, + language: 'en', + nativePriceRequest: 'USD', + nativeCurrency: 'USD', + network: 'mainnet', + prices: {}, + transactions: [], + uniqueTokens: [], +}; + +export default (state = INITIAL_ACCOUNT_STATE, action) => { + switch (action.type) { + case ACCOUNT_UPDATE_ACCOUNT_ADDRESS: + return { + ...state, + accountType: action.payload.accountType, + accountAddress: action.payload.accountAddress, + transactions: [], + }; + case ACCOUNT_GET_ACCOUNT_TRANSACTIONS_REQUEST: + return { + ...state, + fetchingTransactions: action.payload.fetchingTransactions, + transactions: action.payload.transactions, + }; + case ACCOUNT_GET_ACCOUNT_TRANSACTIONS_NO_NEW_PAYLOAD_SUCCESS: + return { + ...state, + fetchingTransactions: false, + }; + case ACCOUNT_GET_ACCOUNT_TRANSACTIONS_SUCCESS: + return { + ...state, + fetchingTransactions: false, + transactions: action.payload, + }; + case ACCOUNT_GET_ACCOUNT_TRANSACTIONS_FAILURE: + return { ...state, fetchingTransactions: false }; + case ACCOUNT_CHECK_TRANSACTION_STATUS_SUCCESS: + return { + ...state, + transactions: action.payload, + }; + case ACCOUNT_UPDATE_TRANSACTIONS_SUCCESS: + return { + ...state, + transactions: action.payload, + }; + case ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_REQUEST: + return { + ...state, + fetchingUniqueTokens: true, + }; + case ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_SUCCESS: + return { + ...state, + fetchingUniqueTokens: false, + uniqueTokens: action.payload, + }; + case ACCOUNT_GET_ACCOUNT_UNIQUE_TOKENS_FAILURE: + return { ...state, fetchingUniqueTokens: false }; + case ACCOUNT_GET_ACCOUNT_BALANCES_REQUEST: + return { + ...state, + fetching: action.payload.fetching, + accountType: action.payload.accountType, + accountInfo: action.payload.accountInfo, + }; + case ACCOUNT_GET_ACCOUNT_BALANCES_SUCCESS: + return { + ...state, + fetching: false, + }; + case ACCOUNT_GET_ACCOUNT_BALANCES_FAILURE: + return { + ...state, + fetching: false, + }; + case ACCOUNT_UPDATE_BALANCES_REQUEST: + return { + ...state, + fetching: true, + }; + case ACCOUNT_UPDATE_BALANCES_SUCCESS: + return { + ...state, + accountInfo: action.payload, + fetching: false, + }; + case ACCOUNT_UPDATE_BALANCES_FAILURE: + return { ...state, fetching: false }; + case ACCOUNT_GET_NATIVE_PRICES_REQUEST: + return { + ...state, + fetchingNativePrices: true, + nativePriceRequest: action.payload, + }; + case ACCOUNT_GET_NATIVE_PRICES_SUCCESS: + return { + ...state, + fetchingNativePrices: false, + nativePriceRequest: '', + prices: action.payload.prices, + accountInfo: action.payload.accountInfo, + }; + case ACCOUNT_GET_NATIVE_PRICES_FAILURE: + return { + ...state, + fetchingNativePrices: false, + nativePriceRequest: 'USD', + }; + case ACCOUNT_INITIALIZE_PRICES_SUCCESS: + return { + ...state, + nativePriceRequest: action.payload.nativeCurrency, + nativeCurrency: action.payload.nativeCurrency, + }; + case ACCOUNT_INITIALIZE_PRICES_FAILURE: + return { + ...state, + }; + case ACCOUNT_UPDATE_HAS_PENDING_TRANSACTION: + return { ...state, hasPendingTransaction: action.payload }; + case ACCOUNT_CHANGE_NATIVE_CURRENCY_SUCCESS: + return { + ...state, + nativeCurrency: action.payload.nativeCurrency, + prices: action.payload.prices, + accountInfo: action.payload.accountInfo, + }; + case ACCOUNT_CHANGE_NATIVE_CURRENCY_FAILURE: + return { + ...state, + }; + case ACCOUNT_CHANGE_LANGUAGE_SUCCESS: + return { + ...state, + language: action.payload.language, + }; + case ACCOUNT_CHANGE_LANGUAGE_FAILURE: + return { + ...state, + }; + case ACCOUNT_CLEAR_STATE: + return { + ...state, + ...INITIAL_ACCOUNT_STATE, + }; + default: + return state; + } +}; diff --git a/src/reducers/_exchange.js b/src/reducers/_exchange.js index 01903aff..b5ba317d 100644 --- a/src/reducers/_exchange.js +++ b/src/reducers/_exchange.js @@ -9,7 +9,7 @@ import { parseError, parseGasPrices, sendTransaction, -} from 'balance-common'; +} from '../handlers/api'; import { web3SendTransactionMultiWallet } from '../handlers/web3'; import { convertAmountFromBigNumber, @@ -21,12 +21,12 @@ import { greaterThanOrEqual, multiply, subtract, -} from 'balance-common'; +} from '../helpers/bignumber'; import { notificationShow } from './_notification'; import { accountUpdateTransactions, accountUpdateHasPendingTransaction, -} from 'balance-common'; +} from '../reducers/_account'; import ethUnits from '../references/ethereum-units.json'; // -- Constants ------------------------------------------------------------- // diff --git a/src/reducers/_ledger.js b/src/reducers/_ledger.js index fa181e5a..b2967790 100644 --- a/src/reducers/_ledger.js +++ b/src/reducers/_ledger.js @@ -2,7 +2,7 @@ import lang from '../languages'; import { accountUpdateAccountAddress, accountUpdateNetwork, -} from 'balance-common'; +} from '../reducers/_account'; import { ledgerEthInit, ledgerEthAccounts } from '../handlers/ledger-eth'; import { notificationShow } from './_notification'; diff --git a/src/reducers/_metamask.js b/src/reducers/_metamask.js index 9569fd20..e3c29514 100644 --- a/src/reducers/_metamask.js +++ b/src/reducers/_metamask.js @@ -1,9 +1,9 @@ -import { parseError } from 'balance-common'; +import { parseError } from '../handlers/parsers'; import { modalClose } from './_modal'; import { accountUpdateAccountAddress, accountUpdateNetwork, -} from 'balance-common'; +} from '../reducers/_account'; import { notificationShow } from './_notification'; import networkList from '../references/ethereum-networks.json'; diff --git a/src/reducers/_send.js b/src/reducers/_send.js new file mode 100644 index 00000000..20b304bd --- /dev/null +++ b/src/reducers/_send.js @@ -0,0 +1,498 @@ +import { get, isEmpty } from 'lodash'; +import { apiGetGasPrices } from '../handlers/api'; +import lang from '../languages'; +import ethUnits from '../references/ethereum-units.json'; +import { + convertAmountFromBigNumber, + convertAmountToBigNumber, + convertAssetAmountFromNativeValue, + convertAssetAmountToNativeValue, + convertAssetAmountToSpecifiedNativeValue, + convertStringToNumber, + formatInputDecimals, + greaterThan, + subtract, +} from '../helpers/bignumber'; +import { + parseError, + parseGasPrices, + parseGasPricesTxFee, +} from '../handlers/parsers'; +import { createSignableTransaction, estimateGasLimit } from '../handlers/web3'; +import { notificationShow } from './_notification'; +import { + accountUpdateTransactions, + accountUpdateHasPendingTransaction, +} from './_account'; + +// -- Constants ------------------------------------------------------------- // + +const SEND_GET_GAS_PRICES_REQUEST = 'send/SEND_GET_GAS_PRICES_REQUEST'; +const SEND_GET_GAS_PRICES_SUCCESS = 'send/SEND_GET_GAS_PRICES_SUCCESS'; +const SEND_GET_GAS_PRICES_FAILURE = 'send/SEND_GET_GAS_PRICES_FAILURE'; + +const SEND_UPDATE_GAS_PRICE_REQUEST = 'send/SEND_UPDATE_GAS_PRICE_REQUEST'; +const SEND_UPDATE_GAS_PRICE_SUCCESS = 'send/SEND_UPDATE_GAS_PRICE_SUCCESS'; +const SEND_UPDATE_GAS_PRICE_FAILURE = 'send/SEND_UPDATE_GAS_PRICE_FAILURE'; + +const SEND_TRANSACTION_REQUEST = 'send/SEND_TRANSACTION_REQUEST'; +const SEND_TRANSACTION_SUCCESS = 'send/SEND_TRANSACTION_SUCCESS'; +const SEND_TRANSACTION_FAILURE = 'send/SEND_TRANSACTION_FAILURE'; + +const SEND_TOGGLE_CONFIRMATION_VIEW = 'send/SEND_TOGGLE_CONFIRMATION_VIEW'; + +const SEND_UPDATE_NATIVE_AMOUNT = 'send/SEND_UPDATE_NATIVE_AMOUNT'; + +const SEND_UPDATE_RECIPIENT = 'send/SEND_UPDATE_RECIPIENT'; +const SEND_UPDATE_ASSET_AMOUNT = 'send/SEND_UPDATE_ASSET_AMOUNT'; +const SEND_UPDATE_SELECTED = 'send/SEND_UPDATE_SELECTED'; +const SEND_UPDATE_HAS_PENDING_TRANSACTION = + 'send/SEND_UPDATE_HAS_PENDING_TRANSACTION'; + +const SEND_CLEAR_FIELDS = 'send/SEND_CLEAR_FIELDS'; + +function getBalanceAmount(accountInfo, gasPrice, selected) { + let amount = ''; + + if (selected.symbol === 'ETH') { + const ethereum = accountInfo.assets.filter( + asset => asset.symbol === 'ETH', + )[0]; + const balanceAmount = ethereum.balance.amount; + const txFeeAmount = gasPrice.txFee.value.amount; + const remaining = convertStringToNumber( + subtract(balanceAmount, txFeeAmount), + ); + amount = convertAmountFromBigNumber(remaining < 0 ? '0' : remaining); + } else { + amount = convertAmountFromBigNumber(selected.balance.amount); + } + + return amount; +} + +// -- Actions --------------------------------------------------------------- // + +export const sendModalInit = (options = {}) => (dispatch, getState) => { + const { accountAddress, accountInfo, prices } = getState().account; + const { gasLimit } = getState().send; + + const fallbackGasPrices = parseGasPrices( + null, + prices, + gasLimit, + options.gasFormat === 'short', + ); + const assets = get(accountInfo, 'assets', []); + const selected = + assets.filter(asset => asset.symbol === options.defaultAsset)[0] || {}; + + dispatch({ + type: SEND_GET_GAS_PRICES_REQUEST, + payload: { + address: accountAddress, + gasPrices: fallbackGasPrices, + selected, + }, + }); + + apiGetGasPrices() + .then(({ data }) => { + const gasPrices = parseGasPrices( + data, + prices, + gasLimit, + options.gasFormat === 'short', + ); + dispatch({ + type: SEND_GET_GAS_PRICES_SUCCESS, + payload: gasPrices, + }); + }) + .catch(error => { + console.error(error); + + dispatch({ + type: SEND_GET_GAS_PRICES_FAILURE, + payload: fallbackGasPrices, + }); + }); +}; + +export const sendUpdateGasPrice = newGasPriceOption => (dispatch, getState) => { + const { + selected, + address, + recipient, + assetAmount, + gasPrice, + gasPriceOption, + fetchingGasPrices, + } = getState().send; + + if (isEmpty(selected)) return; + if (fetchingGasPrices) return; + let gasPrices = getState().send.gasPrices; + if (!Object.keys(gasPrices).length) return null; + const _gasPriceOption = newGasPriceOption || gasPriceOption; + const _gasPrice = gasPriceOption ? gasPrices[_gasPriceOption] : gasPrice; + dispatch({ type: SEND_UPDATE_GAS_PRICE_REQUEST }); + estimateGasLimit({ + asset: selected, + address, + recipient, + amount: assetAmount, + }) + .then(gasLimit => { + const { accountInfo, prices } = getState().account; + gasPrices = parseGasPricesTxFee(gasPrices, prices, gasLimit); + + const ethereum = accountInfo.assets.filter( + asset => asset.symbol === 'ETH', + )[0]; + + const balanceAmount = ethereum.balance.amount; + const txFeeAmount = _gasPrice.txFee.value.amount; + + dispatch({ + type: SEND_UPDATE_GAS_PRICE_SUCCESS, + payload: { + gasLimit, + gasPrice: _gasPrice, + gasPriceOption: _gasPriceOption, + gasPrices, + isSufficientGas: Number(balanceAmount) > Number(txFeeAmount), + }, + }); + }) + .catch(error => { + const message = parseError(error); + if (assetAmount) { + const requestedAmount = convertAmountToBigNumber(`${assetAmount}`); + const availableBalance = get(selected, 'balance.amount'); + if (greaterThan(requestedAmount, availableBalance)) { + dispatch( + notificationShow( + lang.t('notification.error.insufficient_balance'), + true, + ), + ); + } + } else { + dispatch( + notificationShow( + message || lang.t('notification.error.failed_get_tx_fee'), + true, + ), + ); + } + dispatch({ + type: SEND_UPDATE_GAS_PRICE_FAILURE, + payload: { + gasPrice: _gasPrice, + gasPriceOption: _gasPriceOption, + gasPrices: gasPrices, + }, + }); + }); +}; + +export const sendTransaction = ( + transactionDetails, + signAndSendTransactionCb, +) => (dispatch, getState) => + new Promise((resolve, reject) => { + dispatch({ type: SEND_TRANSACTION_REQUEST }); + const { + address, + recipient, + amount, + asset, + gasPrice, + gasLimit, + } = transactionDetails; + const { accountType } = getState().account; + const { selected, trackingAmount } = getState().send; + const txDetails = { + asset: asset, + from: address, + to: recipient, + nonce: null, + amount: amount, + gasPrice: gasPrice.value.amount, + gasLimit: gasLimit, + }; + return createSignableTransaction(txDetails) + .then(signableTransactionDetails => { + const symbol = get(selected, 'symbol', 'unknown'); + const address = get(selected, 'address', ''); + const trackingName = `${symbol}:${address}`; + signAndSendTransactionCb({ + accountType, + tracking: { + action: 'send', + amount: parseFloat(trackingAmount), + name: trackingName, + }, + transaction: signableTransactionDetails, + }) + .then(txHash => { + // has pending transactions set to true for redirect to Transactions route + if (txHash) { + dispatch(accountUpdateHasPendingTransaction()); + } + + txDetails.hash = txHash; + + dispatch(accountUpdateTransactions(txDetails)) + .then(success => { + dispatch({ + type: SEND_TRANSACTION_SUCCESS, + payload: txHash, + }); + resolve(txHash); + }) + .catch(error => { + reject(error); + }); + }) + .catch(error => { + const message = parseError(error); + dispatch(notificationShow(message, true)); + dispatch({ type: SEND_TRANSACTION_FAILURE }); + reject(error); + }); + }) + .catch(error => { + const message = parseError(error); + dispatch(notificationShow(message, true)); + dispatch({ type: SEND_TRANSACTION_FAILURE }); + reject(error); + }); + }); + +export const sendToggleConfirmationView = boolean => (dispatch, getState) => { + let confirm = boolean; + if (!confirm) { + confirm = !getState().send.confirm; + } + dispatch({ type: SEND_TOGGLE_CONFIRMATION_VIEW, payload: confirm }); +}; + +export const sendUpdateRecipient = recipient => dispatch => { + const input = recipient.replace(/[^\w.]/g, ''); + dispatch({ + type: SEND_UPDATE_RECIPIENT, + payload: input, + }); +}; + +export const sendUpdateAssetAmount = assetAmount => (dispatch, getState) => { + const { accountInfo, prices, nativeCurrency } = getState().account; + const { gasPrice, selected } = getState().send; + const _assetAmount = assetAmount.replace(/[^0-9.]/g, ''); + let _nativeAmount = ''; + let _trackingAmount = ''; + if (_assetAmount.length && prices[nativeCurrency][selected.symbol]) { + const nativeAmount = convertAssetAmountToNativeValue( + _assetAmount, + selected, + prices, + ); + _nativeAmount = formatInputDecimals(nativeAmount, _assetAmount); + _trackingAmount = _nativeAmount; + if (nativeCurrency !== 'USD') { + const trackingAmount = convertAssetAmountToSpecifiedNativeValue( + _assetAmount, + selected, + prices, + ); + _trackingAmount = formatInputDecimals(trackingAmount, _assetAmount); + } + } + + const balanceAmount = getBalanceAmount(accountInfo, gasPrice, selected); + dispatch({ + type: SEND_UPDATE_ASSET_AMOUNT, + payload: { + assetAmount: _assetAmount, + nativeAmount: _nativeAmount, + trackingAmount: _trackingAmount, + isSufficientBalance: Number(_assetAmount) <= Number(balanceAmount), + }, + }); +}; + +export const sendUpdateNativeAmount = nativeAmount => (dispatch, getState) => { + const { accountInfo, prices, nativeCurrency } = getState().account; + const { gasPrice, selected } = getState().send; + const _nativeAmount = nativeAmount.replace(/[^0-9.]/g, ''); + let _assetAmount = ''; + let _trackingAmount = ''; + if (_nativeAmount.length && prices[nativeCurrency][selected.symbol]) { + const assetAmount = convertAssetAmountFromNativeValue( + _nativeAmount, + selected, + prices, + ); + _assetAmount = formatInputDecimals(assetAmount, _nativeAmount); + + _trackingAmount = _nativeAmount; + if (nativeCurrency !== 'USD') { + const trackingAmount = convertAssetAmountToSpecifiedNativeValue( + _assetAmount, + selected, + prices, + ); + _trackingAmount = formatInputDecimals(trackingAmount, _assetAmount); + } + } + + const balanceAmount = getBalanceAmount(accountInfo, gasPrice, selected); + + dispatch({ + type: SEND_UPDATE_ASSET_AMOUNT, + payload: { + assetAmount: _assetAmount, + trackingAmount: _trackingAmount, + nativeAmount: _nativeAmount, + isSufficientBalance: Number(_assetAmount) <= Number(balanceAmount), + }, + }); +}; + +export const sendUpdateSelected = value => (dispatch, getState) => { + const state = getState(); + const assetAmount = get(state, 'send.assetAmount', 0); + const assets = get(state, 'account.accountInfo.assets', []); + const nativeCurrency = get(state, 'account.nativeCurrency', ''); + const prices = get(state, 'account.prices', {}); + const selected = assets.filter(asset => asset.symbol === value)[0] || {}; + + dispatch({ type: SEND_UPDATE_SELECTED, payload: selected }); + dispatch(sendUpdateGasPrice()); + + if (prices[nativeCurrency] && prices[nativeCurrency][selected.symbol]) { + dispatch(sendUpdateAssetAmount(assetAmount)); + } +}; + +export const sendMaxBalance = () => (dispatch, getState) => { + const { selected, gasPrice } = getState().send; + const { accountInfo } = getState().account; + const balanceAmount = getBalanceAmount(accountInfo, gasPrice, selected); + + dispatch(sendUpdateAssetAmount(balanceAmount)); +}; + +export const sendClearFields = () => ({ type: SEND_CLEAR_FIELDS }); + +// -- Reducer --------------------------------------------------------------- // +const INITIAL_STATE = { + fetchingGasPrices: false, + gasPrice: {}, + gasPrices: {}, + gasLimit: ethUnits.basic_tx, + gasPriceOption: 'average', + fetching: false, + address: '', + recipient: '', + nativeAmount: '', + trackingAmount: '', + assetAmount: '', + txHash: '', + confirm: false, + selected: {}, +}; + +export default (state = INITIAL_STATE, action) => { + switch (action.type) { + case SEND_GET_GAS_PRICES_REQUEST: + return { + ...state, + fetchingGasPrices: true, + address: action.payload.address, + selected: action.payload.selected, + gasPrice: action.payload.gasPrices.average, + gasPrices: action.payload.gasPrices, + gasPriceOption: action.payload.gasPrices.average.option, + }; + case SEND_GET_GAS_PRICES_SUCCESS: + return { + ...state, + fetchingGasPrices: false, + gasPrice: action.payload.average, + gasPrices: action.payload, + gasPriceOption: action.payload.average.option, + }; + case SEND_GET_GAS_PRICES_FAILURE: + return { + ...state, + fetchingGasPrices: false, + gasPrice: action.payload.average, + gasPrices: action.payload, + gasPriceOption: action.payload.average.option, + }; + case SEND_UPDATE_GAS_PRICE_REQUEST: + return { ...state, fetchingGasPrices: true }; + case SEND_UPDATE_GAS_PRICE_SUCCESS: + return { + ...state, + fetchingGasPrices: false, + gasLimit: action.payload.gasLimit, + gasPrice: action.payload.gasPrice, + gasPrices: action.payload.gasPrices, + gasPriceOption: action.payload.gasPriceOption, + isSufficientGas: action.payload.isSufficientGas, + }; + + case SEND_UPDATE_GAS_PRICE_FAILURE: + return { + ...state, + fetchingGasPrices: false, + gasPrice: action.payload.gasPrice, + gasPrices: action.payload.gasPrices, + gasPriceOption: action.payload.gasPriceOption, + }; + case SEND_TRANSACTION_REQUEST: + return { ...state, fetching: true }; + case SEND_TRANSACTION_SUCCESS: + return { + ...state, + fetching: false, + gasPrices: {}, + txHash: action.payload, + }; + case SEND_TRANSACTION_FAILURE: + return { + ...state, + fetching: false, + txHash: '', + confirm: false, + }; + case SEND_UPDATE_HAS_PENDING_TRANSACTION: + return { ...state, hasPendingTransaction: action.payload }; + case SEND_TOGGLE_CONFIRMATION_VIEW: + return { + ...state, + confirm: action.payload, + }; + case SEND_UPDATE_RECIPIENT: + return { ...state, recipient: action.payload }; + case SEND_UPDATE_NATIVE_AMOUNT: + case SEND_UPDATE_ASSET_AMOUNT: + return { + ...state, + assetAmount: action.payload.assetAmount, + nativeAmount: action.payload.nativeAmount, + trackingAmount: action.payload.trackingAmount, + isSufficientBalance: action.payload.isSufficientBalance, + }; + case SEND_UPDATE_SELECTED: + return { ...state, selected: action.payload }; + case SEND_CLEAR_FIELDS: + return { ...state, ...INITIAL_STATE }; + default: + return state; + } +}; diff --git a/src/reducers/_trezor.js b/src/reducers/_trezor.js index e6460953..1a1fce7d 100644 --- a/src/reducers/_trezor.js +++ b/src/reducers/_trezor.js @@ -2,7 +2,7 @@ import lang from '../languages'; import { accountUpdateAccountAddress, accountUpdateNetwork, -} from 'balance-common'; +} from '../reducers/_account'; import { trezorEthInit, trezorEthAccounts } from '../handlers/trezor-eth'; import { notificationShow } from './_notification'; diff --git a/src/reducers/_walletconnect.js b/src/reducers/_walletconnect.js index b4e0d3fe..492026e3 100644 --- a/src/reducers/_walletconnect.js +++ b/src/reducers/_walletconnect.js @@ -3,7 +3,7 @@ import WalletConnectQRCodeModal from '@walletconnect/qrcode-modal'; import { accountUpdateAccountAddress, accountUpdateNetwork, -} from 'balance-common'; +} from '../reducers/_account'; import { notificationShow } from './_notification'; import chains from '../references/chains.json'; diff --git a/src/reducers/index.js b/src/reducers/index.js index b82e47a0..6803e9be 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -1,14 +1,15 @@ import { combineReducers } from 'redux'; +import account from './_account'; import exchange from './_exchange'; import modal from './_modal'; import ledger from './_ledger'; +import send from './_send'; import trezor from './_trezor'; import metamask from './_metamask'; import walletconnect from './_walletconnect'; import notification from './_notification'; import warning from './_warning'; import zrxinstant from './_zrxinstant'; -import { account, send } from 'balance-common'; export default combineReducers({ exchange, diff --git a/src/views/Account/AccountBalances.js b/src/views/Account/AccountBalances.js index 3ec4d09c..35aa647e 100644 --- a/src/views/Account/AccountBalances.js +++ b/src/views/Account/AccountBalances.js @@ -5,12 +5,12 @@ import styled from 'styled-components'; import AssetIcon from '../../components/AssetIcon'; import ToggleIndicator from '../../components/ToggleIndicator'; import lang from '../../languages'; +import { ellipseText } from '../../helpers/utilities'; import { convertStringToNumber, - ellipseText, hasHighMarketValue, hasLowMarketValue, -} from 'balance-common'; +} from '../../helpers/bignumber'; import { colors, fonts, responsive } from '../../styles'; const StyledGrid = styled.div` diff --git a/src/views/Account/AccountTransactions.js b/src/views/Account/AccountTransactions.js index 3aa44681..c6f522b1 100644 --- a/src/views/Account/AccountTransactions.js +++ b/src/views/Account/AccountTransactions.js @@ -13,10 +13,8 @@ import ToggleIndicator from '../../components/ToggleIndicator'; import TransactionStatus from '../../components/TransactionStatus'; import etherscanLogo from '../../assets/etherscan-logo.svg'; import ethplorerLogo from '../../assets/ethplorer-logo.svg'; -import { - accountUpdateHasPendingTransaction, - getLocalTimeDate, -} from 'balance-common'; +import { accountUpdateHasPendingTransaction } from '../../reducers/_account'; +import { getLocalTimeDate } from '../../helpers/time'; import { colors, fonts, shadows, responsive } from '../../styles'; const StyledGrid = styled.div` diff --git a/src/views/Account/index.js b/src/views/Account/index.js index 40913d8a..00049c27 100644 --- a/src/views/Account/index.js +++ b/src/views/Account/index.js @@ -5,7 +5,7 @@ import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import styled from 'styled-components'; import lang from '../../languages'; -import { capitalize } from 'balance-common'; +import { capitalize } from '../../helpers/utilities'; import TabMenu from '../../components/TabMenu'; import Card from '../../components/Card'; import Button from '../../components/Button'; From 270c573181d95d3f8110d802abb9127512fada19 Mon Sep 17 00:00:00 2001 From: Jack Leslie Date: Mon, 3 Jun 2019 00:19:43 +0100 Subject: [PATCH 3/4] Removed Exchange modal and reducers, removed balance common --- package.json | 1 - src/modals/ExchangeModal/index.js | 1012 ----------------------------- src/modals/index.js | 7 - src/reducers/_exchange.js | 910 -------------------------- src/reducers/index.js | 2 - yarn.lock | 234 +------ 6 files changed, 2 insertions(+), 2164 deletions(-) delete mode 100644 src/modals/ExchangeModal/index.js delete mode 100644 src/reducers/_exchange.js diff --git a/package.json b/package.json index a68449ec..8207be86 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "@walletconnect/browser": "^1.0.0-beta.18", "@walletconnect/qrcode-modal": "^1.0.0-beta.18", "axios": "^0.18.0", - "balance-common": "../balance-common/", "bignumber.js": "^7.0.1", "bowser": "^2.0.0-alpha.4", "ethereumjs-tx": "^1.3.4", diff --git a/src/modals/ExchangeModal/index.js b/src/modals/ExchangeModal/index.js deleted file mode 100644 index 6090f532..00000000 --- a/src/modals/ExchangeModal/index.js +++ /dev/null @@ -1,1012 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import { connect } from 'react-redux'; -import styled from 'styled-components'; -import lang from '../../languages'; -import Card from '../../components/Card'; -import Input from '../../components/Input'; -import LineBreak from '../../components/LineBreak'; -import AccountType from '../../components/AccountType'; -import AssetIcon from '../../components/AssetIcon'; -import DropdownAsset from '../../components/DropdownAsset'; -import Button from '../../components/Button'; -import Form from '../../components/Form'; -import exchangeIcon from '../../assets/exchange-icon.svg'; -import arrowUp from '../../assets/arrow-up.svg'; -import { modalClose } from '../../reducers/_modal'; -import { - exchangeClearFields, - exchangeModalInit, - exchangeSendTransaction, - exchangeUpdateWithdrawalAmount, - exchangeUpdateWithdrawalNative, - exchangeUpdateDepositAmount, - exchangeUpdateDepositSelected, - exchangeUpdateWithdrawalSelected, - exchangeToggleConfirmationView, - exchangeConfirmTransaction, - exchangeToggleWithdrawalNative, - exchangeMaxBalance, -} from '../../reducers/_exchange'; -import { notificationShow } from '../../reducers/_notification'; -import { capitalize } from '../../helpers/utilities'; -import { - add, - convertAmountFromBigNumber, - convertAmountToDisplay, - convertNumberToString, - multiply, - divide, - greaterThan, - smallerThan, - convertAmountToBigNumber, - handleSignificantDecimals, -} from '../../helpers/bignumber'; -import { getCountdown } from '../../helpers/time'; -import { fonts, colors, responsive, transitions } from '../../styles'; - -import { - StyledAmountCurrency, - StyledConversionContainer, - StyledConversionIconContainer, - StyledInputContainer, -} from '../modalStyles'; - -const StyledSuccessMessage = styled.div` - width: 100%; - padding: 22px; - & a { - text-decoration: underline; - font-weight: ${fonts.weight.semibold}; - } - & > *:nth-child(n + 2) { - margin-top: 24px; - } -`; - -const StyledIcon = styled.div` - width: 14px; - height: 14px; - mask: ${({ icon }) => (icon ? `url(${icon}) center no-repeat` : 'none')}; - mask-size: 90%; - background-color: ${({ color }) => - color ? `rgb(${colors[color]})` : `rgb(${colors.dark})`}; -`; - -const StyledFlex = styled.div` - width: ${({ spanWidth }) => (spanWidth ? '100%' : 'auto')}; - display: flex; - flex-grow: 1; - align-items: flex-start; - position: relative; - transform: none; -`; - -const StyledColumn = styled(StyledFlex)` - flex-direction: column; - align-items: center; - justify-content: center; -`; - -const StyledDropdownLabel = styled.p` - color: rgb(${colors.grey}); - font-size: 13px; - font-weight: ${fonts.weight.semibold}; - margin-bottom: 8px; -`; - -const StyledHelperWrapper = styled.div` - width: 100%; - display: flex; - flex-direction: column; -`; - -const StyledHelperContainer = styled.div` - width: 100%; - display: grid; - grid-template-columns: 1fr 1fr; - padding-top: ${({ noPadding }) => (noPadding ? '0' : '16px')}; -`; - -const StyledHelperText = styled.div` - width: 100%; - text-align: left; - opacity: ${({ fetching }) => (fetching ? `0.5` : `1.0`)}; - cursor: ${({ onClick }) => - typeof onClick !== 'undefined' ? 'pointer' : 'default'}; - ${({ onClick }) => - typeof onClick !== 'undefined' && - ` - &:hover { - opacity: 0.7; - } - `}; - - &:not(:first-child) { - padding-left: 8px; - } - - @media screen and (${responsive.xs.max}) { - text-align: ${({ centerOnMobile }) => (centerOnMobile ? 'center' : 'left')}; - } - - & p { - color: ${({ warn }) => - warn ? `rgb(${colors.red})` : `rgb(${colors.grey})`}; - font-size: 13px; - font-weight: ${fonts.weight.normal}; - } - - & strong { - color: ${({ warn }) => - warn ? `rgb(${colors.red})` : `rgb(${colors.grey})`}; - font-size: 13px; - font-weight: ${fonts.weight.semibold}; - margin-bottom: 8px; - } -`; - -const StyledBottomModal = styled(StyledFlex)` - & p { - font-size: ${fonts.size.h6}; - } - & > * { - width: 100%; - } -`; - -const StyledParagraph = styled.p` - margin: 10px 0; - color: rgb(${colors.grey}); - font-weight: ${fonts.weight.medium}; -`; - -const StyledHash = styled.p` - font-size: ${fonts.size.small}; - font-weight: 600; - text-align: center; - letter-spacing: -0.4px; - background: rgb(${colors.white}); - border-radius: 8px; - margin: 0 auto; - padding: 12px 18px; -`; - -const StyledStaticCurrency = styled(StyledAmountCurrency)` - cursor: default; - color: rgb(${colors.dark}); -`; - -const StyledDynamicCurrencyContainer = styled.div` - position: absolute; - right: 6px; -`; - -const StyledDynamicCurrency = styled(StyledAmountCurrency)` - position: relative; - display: inline-block; - cursor: pointer; - transition: ${transitions.short}; - color: ${({ selected }) => - selected ? `rgb(${colors.white})` : `rgba(${colors.darkGrey}, 0.7)`}; - background: ${({ fetching, selected }) => - fetching - ? selected - ? `rgba(${colors.dark}, 0.5)` - : `none` - : selected - ? `rgb(${colors.dark})` - : `rgb(${colors.white})`}; - &:hover { - background: ${({ disabled, fetching, selected }) => - !fetching && !selected && !disabled && `rgb(${colors.dark}, 0.1)`}; - } -`; - -const StyledNativeCurrency = styled(StyledDynamicCurrency)` - margin-right: 4px; -`; - -const StyledExchangeIconContainer = styled(StyledConversionIconContainer)` - align-self: flex-start; - height: 94px; - - @media screen and (${responsive.sm.max}) { - height: 88px; - } - - @media screen and (${responsive.xs.max}) { - height: inherit; - } -`; - -const StyledExchangeIcon = styled.img` - height: 15px; - width: 20px; - margin: 0 16px; - - @media screen and (${responsive.xs.max}) { - margin: 16px 0 0; - } -`; - -const StyledSubTitle = styled.div` - display: flex; - align-items: center; - color: rgb(${colors.grey}); - font-size: ${fonts.size.h6}; - font-weight: ${fonts.weight.semibold}; - width: 100%; - & ${StyledIcon} { - margin-right: 5px; - } -`; - -const StyledMaxBalance = styled.p` - position: absolute; - cursor: pointer; - top: 0; - right: 0; - line-height: 1.8em; - font-size: ${fonts.size.small}; - font-weight: ${fonts.weight.medium}; - color: rgba(${colors.blue}, 0.8); - @media (hover: hover) { - &:hover { - opacity: 0.6; - } - } -`; - -const StyledApproveTransaction = styled.div` - padding: 22px; - display: flex; - flex-direction: column; - align-items: center; - & > div { - margin-top: 15px; - } - & > p { - margin-top: 32px; - } -`; - -export const StyledActions = styled.div` - display: flex; - align-items: center; - flex-wrap: wrap; - justify-content: ${({ single }) => (single ? `center` : `space-between`)}; - - & button { - margin: 0 5px; - } - - @media screen and (${responsive.sm.max}) { - > div { - order: 1; - width: 100%; - margin-bottom: 8px; - text-align: center; - } - - button { - &:first-child { - order: 2; - } - - &:last-child { - order: 3; - } - } - } - - @media screen and (${responsive.xxs.max}) { - > div { - margin-bottom: 16px; - } - } -`; - -const StyledFees = styled.div` - display: flex; - flex-direction: column; - align-items: center; - ${StyledHelperText} { - text-align: center; - } -`; - -const StyledMessage = styled.div` - display: flex; - align-items: center; - justify-content: center; - color: rgb(${colors.grey}); - font-weight: ${fonts.weight.medium}; -`; - -const StyledBlockedMessage = StyledMessage.extend` - min-height: 400px; - padding: 0 20px 12px 20px; - text-align: center; - & a { - color: #657fe6 !important; - } -`; - -class ExchangeModal extends Component { - state = { - activeInput: '', - showWithdrawalNative: false, - }; - componentDidMount() { - this.props.exchangeModalInit(); - } - onChangeDepositSelected = value => - this.props.exchangeUpdateDepositSelected(value); - onChangeWithdrawalSelected = value => - this.props.exchangeUpdateWithdrawalSelected(value); - onChangeDepositInput = ({ target }) => - this.props.exchangeUpdateDepositAmount(target.value); - onChangeWithdrawalInput = ({ target }) => { - if (this.props.showWithdrawalNative) { - this.props.exchangeUpdateWithdrawalNative(target.value); - } else { - this.props.exchangeUpdateWithdrawalAmount(target.value); - } - }; - onExchangeMin = () => { - const exchangeDetails = - Object.keys(this.props.exchangeDetails).length && - this.props.exchangeDetails.quotedRate - ? this.props.exchangeDetails - : null; - if (exchangeDetails) { - this.props.exchangeUpdateDepositAmount(exchangeDetails.min); - } - }; - onToggleWithdrawalNative = bool => { - if (this.props.fetchingRate && this.state.activeInput !== 'WITHDRAWAL') - return; - this.props.exchangeToggleWithdrawalNative(bool); - }; - onExchangeMaxBalance = () => this.props.exchangeMaxBalance(); - onExchangeAnother = () => { - this.props.exchangeToggleConfirmationView(); - this.props.exchangeClearFields(); - this.props.exchangeModalInit(); - }; - onGoBack = () => this.props.exchangeToggleConfirmationView(); - onFocus = activeInput => this.setState({ activeInput }); - onBlur = () => this.setState({ activeInput: '' }); - onSubmit = e => { - e.preventDefault(); - if (!this.props.gasPrice.txFee) { - this.props.notificationShow( - lang.t('notification.error.generic_error'), - true, - ); - return; - } - if (!this.props.confirm) { - if (this.props.depositSelected.symbol === 'ETH') { - const ethereum = this.props.accountInfo.assets.filter( - asset => asset.symbol === 'ETH', - )[0]; - const balanceAmount = ethereum.balance.amount; - const balance = convertAmountFromBigNumber(balanceAmount); - const requestedAmount = convertNumberToString(this.props.depositAmount); - const txFeeAmount = this.props.gasPrice.txFee.value.amount; - const txFee = convertAmountFromBigNumber(txFeeAmount); - const includingFees = add(requestedAmount, txFee); - if (greaterThan(requestedAmount, balance)) { - this.props.notificationShow( - lang.t('notification.error.insufficient_balance'), - true, - ); - return; - } else if (greaterThan(includingFees, balance)) { - this.props.notificationShow( - lang.t('notification.error.insufficient_for_fees'), - true, - ); - return; - } - } else { - const ethereum = this.props.accountInfo.assets.filter( - asset => asset.symbol === 'ETH', - )[0]; - const etherBalanceAmount = ethereum.balance.amount; - const etherBalance = convertAmountFromBigNumber(etherBalanceAmount); - const tokenBalanceAmount = this.props.depositSelected.balance.amount; - const tokenBalance = convertAmountFromBigNumber(tokenBalanceAmount); - const requestedAmount = convertNumberToString(this.props.depositAmount); - const includingFees = convertAmountFromBigNumber( - this.props.gasPrice.txFee.value.amount, - ); - if (greaterThan(requestedAmount, tokenBalance)) { - this.props.notificationShow( - lang.t('notification.error.insufficient_balance'), - true, - ); - return; - } else if (greaterThan(includingFees, etherBalance)) { - this.props.notificationShow( - lang.t('notification.error.insufficient_for_fees'), - true, - ); - return; - } - } - this.props.exchangeConfirmTransaction(); - } else { - this.props.exchangeSendTransaction(); - } - }; - onClose = () => { - this.props.exchangeClearFields(); - this.props.modalClose(); - }; - render = () => { - const withdrawalAssets = this.props.withdrawalAssets.filter( - asset => asset.symbol !== this.props.depositSelected.symbol, - ); - const balance = this.props.accountInfo.assets.filter( - asset => asset.symbol === this.props.depositSelected.symbol, - )[0].balance.display; - const depositNative = this.props.accountInfo.assets.filter( - asset => asset.symbol === this.props.depositSelected.symbol, - )[0].native; - const depositValue = depositNative - ? this.props.accountInfo.assets.filter( - asset => asset.symbol === this.props.depositSelected.symbol, - )[0].native.balance.display - : ''; - const exchangeDetails = - Object.keys(this.props.exchangeDetails).length && - this.props.exchangeDetails.quotedRate - ? this.props.exchangeDetails - : null; - const rate = exchangeDetails - ? convertAmountToDisplay( - convertAmountToBigNumber(divide(1, exchangeDetails.quotedRate)), - null, - this.props.depositSelected, - ) - : null; - const depositPrices = - exchangeDetails && - this.props.prices[this.props.nativeCurrency] && - this.props.prices[this.props.nativeCurrency][ - this.props.depositSelected.symbol - ] && - Object.keys(exchangeDetails).length - ? this.props.prices[this.props.nativeCurrency][ - this.props.depositSelected.symbol - ] - : null; - const depositMin = exchangeDetails - ? convertAmountToDisplay( - convertAmountToBigNumber(exchangeDetails.min), - null, - this.props.depositSelected, - ) - : null; - const depositMax = exchangeDetails - ? convertAmountToDisplay( - convertAmountToBigNumber(exchangeDetails.maxLimit), - null, - this.props.depositSelected, - ) - : null; - const depositUnder = exchangeDetails - ? this.props.depositAmount !== '' - ? smallerThan(this.props.depositAmount, exchangeDetails.min) - : false - : false; - const depositOver = exchangeDetails - ? this.props.depositAmount !== '' - ? greaterThan(this.props.depositAmount, exchangeDetails.maxLimit) - : false - : false; - const exchangeFeeValue = exchangeDetails - ? convertAmountToDisplay( - convertAmountToBigNumber(exchangeDetails.minerFee), - null, - this.props.withdrawalSelected, - ) - : null; - const exchangeFeeNative = - exchangeDetails && depositPrices - ? convertAmountToDisplay( - convertAmountToBigNumber( - multiply( - exchangeDetails.minerFee, - multiply( - convertAmountFromBigNumber(depositPrices.price.amount), - divide(1, exchangeDetails.quotedRate), - ), - ), - ), - this.props.prices, - ) - : null; - const withdrawalValue = - this.props.showWithdrawalNative && this.props.withdrawalAmount - ? `${handleSignificantDecimals(this.props.withdrawalAmount, 8)} ${ - this.props.withdrawalSelected.symbol - }` - : this.props.withdrawalAmount && - this.props.withdrawalPrice && - this.props.withdrawalPrice.amount - ? convertAmountToDisplay( - convertAmountFromBigNumber( - multiply( - convertAmountToBigNumber(this.props.withdrawalAmount), - this.props.withdrawalPrice.amount, - ), - ), - this.props.prices, - ) - : null; - return ( - - {this.props.shapeshiftAvailable ? ( - !this.props.txHash ? ( - !this.props.confirm ? ( -
- - - {lang.t('modal.exchange_title', { - walletName: capitalize( - `${this.props.accountType}${lang.t( - 'modal.default_wallet', - )}`, - ), - })} - - - - - - {lang.t('modal.deposit_dropdown_label')} - - - - - {lang.t('modal.helper_balance')} -

{balance || '———'}

-
- - {lang.t('modal.helper_value')} -

{depositValue || '———'}

-
-
-
-
- - - - - - - - - {lang.t('modal.withdrawal_dropdown_label')} - - - - - {lang.t('modal.helper_rate')} -

{rate || '———'}

-
- - {lang.t('modal.helper_price')} -

{this.props.withdrawalPrice.display || '———'}

-
-
-
-
-
- - - - - - this.onFocus('DEPOSIT')} - onBlur={this.onBlur} - monospace - label={lang.t('modal.deposit_input_label')} - placeholder="0.0" - type="text" - value={this.props.depositAmount} - onChange={this.onChangeDepositInput} - > - - {this.props.depositSelected.symbol} - - - - {lang.t('modal.exchange_max')} - - - - - - {lang.t('modal.helper_min')} -

{depositMin || '———'}

-
- - {lang.t('modal.helper_max')} -

{depositMax || '———'}

-
-
-
-
- - - - - - - - - this.onFocus('WITHDRAWAL')} - onBlur={this.onBlur} - monospace - placeholder="0.0" - label={lang.t('modal.withdrawal_input_label')} - type="text" - value={this.props.withdrawalInput} - onChange={this.onChangeWithdrawalInput} - > - - - this.onToggleWithdrawalNative(true) - } - selected={this.props.showWithdrawalNative} - disabled={ - this.props.fetchingRate && - this.state.activeInput !== 'WITHDRAWAL' - } - > - {this.props.prices && this.props.prices.selected - ? this.props.prices.selected.currency - : 'USD'} - - - this.onToggleWithdrawalNative(false) - } - selected={!this.props.showWithdrawalNative} - disabled={ - this.props.fetchingRate && - this.state.activeInput !== 'WITHDRAWAL' - } - > - {this.props.withdrawalSelected.symbol} - - - - - - - {lang.t('modal.helper_value')} -

- {withdrawalValue - ? withdrawalValue - : this.props.showWithdrawalNative - ? `0.00 ${this.props.withdrawalSelected.symbol}` - : `${ - this.props.prices && - this.props.prices.selected - ? this.props.prices.selected.symbol - : '$' - }0.00`} -

-
- -
-
-
-
- - - - - - - - - - {lang.t('modal.tx_fee')} -

{`${ - this.props.nativeCurrency !== 'ETH' - ? `${ - Object.keys(this.props.gasPrice).length && - this.props.gasPrice.txFee - ? this.props.gasPrice.txFee.value.display - : '0.000 ETH' - } ≈ ` - : '' - }${ - Object.keys(this.props.gasPrice).length && - this.props.gasPrice.txFee && - this.props.gasPrice.txFee.native - ? this.props.gasPrice.txFee.native.value.display - : '$0.00' - }`}

-
- - {lang.t('modal.exchange_fee')} -

{`${ - exchangeFeeValue ? `${exchangeFeeValue} ≈ ` : '' - }${ - exchangeFeeNative ? exchangeFeeNative : '$0.00' - }`}

-
-
-
- -
-
- - ) : ( - - - - - {`${this.props.depositAmount} ${ - this.props.depositSelected.symbol - }`} - - - - - - - {`${this.props.withdrawalAmount} ${ - this.props.withdrawalSelected.symbol - }`} - - - - - {`Expiration time: ${getCountdown(this.props.countdown)}`} - - - - - {lang.t('modal.approve_tx', { - walletType: capitalize(this.props.accountType), - })} - - - - - - ) - ) : ( - - - - {`Success`} - -
- - {`${lang.t('modal.tx_hash')}:`} - - {` ${this.props.txHash}`} -
- - - {lang.t('modal.tx_verify')} - - - - - - -
- ) - ) : ( - - {lang.t('message.exchange_not_available')} - - )} -
- ); - }; -} - -ExchangeModal.propTypes = { - modalClose: PropTypes.func.isRequired, - exchangeClearFields: PropTypes.func.isRequired, - exchangeModalInit: PropTypes.func.isRequired, - exchangeSendTransaction: PropTypes.func.isRequired, - exchangeUpdateWithdrawalAmount: PropTypes.func.isRequired, - exchangeUpdateWithdrawalNative: PropTypes.func.isRequired, - exchangeUpdateDepositAmount: PropTypes.func.isRequired, - exchangeUpdateDepositSelected: PropTypes.func.isRequired, - exchangeUpdateWithdrawalSelected: PropTypes.func.isRequired, - exchangeToggleConfirmationView: PropTypes.func.isRequired, - exchangeConfirmTransaction: PropTypes.func.isRequired, - exchangeToggleWithdrawalNative: PropTypes.func.isRequired, - exchangeMaxBalance: PropTypes.func.isRequired, - notificationShow: PropTypes.func.isRequired, - fetchingRate: PropTypes.bool.isRequired, - fetchingFinal: PropTypes.bool.isRequired, - fetching: PropTypes.bool.isRequired, - gasPrice: PropTypes.object.isRequired, - address: PropTypes.string.isRequired, - recipient: PropTypes.string.isRequired, - txHash: PropTypes.string.isRequired, - confirm: PropTypes.bool.isRequired, - countdown: PropTypes.string.isRequired, - exchangeDetails: PropTypes.object.isRequired, - depositAssets: PropTypes.array.isRequired, - withdrawalAssets: PropTypes.array.isRequired, - depositSelected: PropTypes.object.isRequired, - withdrawalSelected: PropTypes.object.isRequired, - depositAmount: PropTypes.string.isRequired, - showWithdrawalNative: PropTypes.bool.isRequired, - withdrawalInput: PropTypes.string.isRequired, - withdrawalAmount: PropTypes.string.isRequired, - withdrawalPrice: PropTypes.object.isRequired, - accountInfo: PropTypes.object.isRequired, - accountType: PropTypes.string.isRequired, - network: PropTypes.string.isRequired, - prices: PropTypes.object.isRequired, - nativeCurrency: PropTypes.string.isRequired, - shapeshiftAvailable: PropTypes.bool.isRequired, - fetchingShapeshift: PropTypes.bool.isRequired, -}; - -const reduxProps = ({ modal, exchange, account }) => ({ - fetchingRate: exchange.fetchingRate, - fetchingFinal: exchange.fetchingFinal, - fetching: exchange.fetching, - gasPrice: exchange.gasPrice, - address: exchange.address, - recipient: exchange.recipient, - txHash: exchange.txHash, - confirm: exchange.confirm, - countdown: exchange.countdown, - exchangeDetails: exchange.exchangeDetails, - depositAssets: exchange.depositAssets, - withdrawalAssets: exchange.withdrawalAssets, - depositSelected: exchange.depositSelected, - withdrawalSelected: exchange.withdrawalSelected, - depositAmount: exchange.depositAmount, - showWithdrawalNative: exchange.showWithdrawalNative, - withdrawalInput: exchange.withdrawalInput, - withdrawalAmount: exchange.withdrawalAmount, - withdrawalPrice: exchange.withdrawalPrice, - accountInfo: account.accountInfo, - accountType: account.accountType, - network: account.network, - prices: account.prices, - nativeCurrency: account.nativeCurrency, - shapeshiftAvailable: account.shapeshiftAvailable, - fetchingShapeshift: account.fetchingShapeshift, -}); - -export default connect( - reduxProps, - { - modalClose, - exchangeClearFields, - exchangeModalInit, - exchangeSendTransaction, - exchangeUpdateWithdrawalAmount, - exchangeUpdateWithdrawalNative, - exchangeUpdateDepositAmount, - exchangeUpdateDepositSelected, - exchangeUpdateWithdrawalSelected, - exchangeToggleConfirmationView, - exchangeConfirmTransaction, - exchangeToggleWithdrawalNative, - exchangeMaxBalance, - notificationShow, - }, -)(ExchangeModal); diff --git a/src/modals/index.js b/src/modals/index.js index 24bc0ada..318ad37e 100644 --- a/src/modals/index.js +++ b/src/modals/index.js @@ -5,14 +5,12 @@ import styled from 'styled-components'; import Column from '../components/Column'; -import ExchangeModal from './ExchangeModal'; import SendModal from './SendModal'; import ReceiveModal from './ReceiveModal'; import DonateModal from './DonateModal'; import WalletConnectModal from './WalletConnectModal'; import { modalClose } from '../reducers/_modal'; import { sendClearFields } from '../reducers/_send'; -import { exchangeClearFields } from '../reducers/_exchange'; import { colors, transitions } from '../styles'; @@ -56,14 +54,11 @@ class Modal extends Component { static propTypes = { modalClose: PropTypes.func.isRequired, sendClearFields: PropTypes.func.isRequired, - exchangeClearFields: PropTypes.func.isRequired, modal: PropTypes.string.isRequired, }; modalController = () => { switch (this.props.modal) { - case 'EXCHANGE_MODAL': - return ; case 'SEND_MODAL': return ; case 'DONATION_MODAL': @@ -79,7 +74,6 @@ class Modal extends Component { onClose = () => { this.props.sendClearFields(); - this.props.exchangeClearFields(); this.props.modalClose(); }; @@ -108,6 +102,5 @@ export default connect( { modalClose, sendClearFields, - exchangeClearFields, }, )(Modal); diff --git a/src/reducers/_exchange.js b/src/reducers/_exchange.js deleted file mode 100644 index b5ba317d..00000000 --- a/src/reducers/_exchange.js +++ /dev/null @@ -1,910 +0,0 @@ -import { - apiGetGasPrices, - apiGetSinglePrice, - apiShapeshiftGetMarketInfo, - apiShapeshiftGetCurrencies, - apiShapeshiftSendAmount, - apiShapeshiftGetExchangeDetails, - estimateGasLimit, - parseError, - parseGasPrices, - sendTransaction, -} from '../handlers/api'; -import { web3SendTransactionMultiWallet } from '../handlers/web3'; -import { - convertAmountFromBigNumber, - convertStringToNumber, - convertAmountToBigNumber, - convertAmountToDisplay, - divide, - greaterThan, - greaterThanOrEqual, - multiply, - subtract, -} from '../helpers/bignumber'; -import { notificationShow } from './_notification'; -import { - accountUpdateTransactions, - accountUpdateHasPendingTransaction, -} from '../reducers/_account'; -import ethUnits from '../references/ethereum-units.json'; - -// -- Constants ------------------------------------------------------------- // - -const EXCHANGE_GET_AVAILABLE_REQUEST = - 'exchange/EXCHANGE_GET_AVAILABLE_REQUEST'; -const EXCHANGE_GET_AVAILABLE_SUCCESS = - 'exchange/EXCHANGE_GET_AVAILABLE_SUCCESS'; -const EXCHANGE_GET_AVAILABLE_FAILURE = - 'exchange/EXCHANGE_GET_AVAILABLE_FAILURE'; - -const EXCHANGE_UPDATE_GAS_LIMIT_REQUEST = - 'exchange/EXCHANGE_UPDATE_GAS_LIMIT_REQUEST'; -const EXCHANGE_UPDATE_GAS_LIMIT_SUCCESS = - 'exchange/EXCHANGE_UPDATE_GAS_LIMIT_SUCCESS'; -const EXCHANGE_UPDATE_GAS_LIMIT_FAILURE = - 'exchange/EXCHANGE_UPDATE_GAS_LIMIT_FAILURE'; - -const EXCHANGE_GET_GAS_PRICE_REQUEST = - 'exchange/EXCHANGE_GET_GAS_PRICE_REQUEST'; -const EXCHANGE_GET_GAS_PRICE_SUCCESS = - 'exchange/EXCHANGE_GET_GAS_PRICE_SUCCESS'; -const EXCHANGE_GET_GAS_PRICE_FAILURE = - 'exchange/EXCHANGE_GET_GAS_PRICE_FAILURE'; - -const EXCHANGE_TRANSACTION_REQUEST = 'exchange/EXCHANGE_TRANSACTION_REQUEST'; -const EXCHANGE_TRANSACTION_SUCCESS = 'exchange/EXCHANGE_TRANSACTION_SUCCESS'; -const EXCHANGE_TRANSACTION_FAILURE = 'exchange/EXCHANGE_TRANSACTION_FAILURE'; - -const EXCHANGE_CONFIRM_TRANSACTION_REQUEST = - 'exchange/EXCHANGE_CONFIRM_TRANSACTION_REQUEST'; -const EXCHANGE_CONFIRM_TRANSACTION_SUCCESS = - 'exchange/EXCHANGE_CONFIRM_TRANSACTION_SUCCESS'; -const EXCHANGE_CONFIRM_TRANSACTION_FAILURE = - 'exchange/EXCHANGE_CONFIRM_TRANSACTION_FAILURE'; - -const EXCHANGE_UPDATE_DEPOSIT_AMOUNT_REQUEST = - 'exchange/EXCHANGE_UPDATE_DEPOSIT_AMOUNT_REQUEST'; -const EXCHANGE_UPDATE_DEPOSIT_AMOUNT_SUCCESS = - 'exchange/EXCHANGE_UPDATE_DEPOSIT_AMOUNT_SUCCESS'; -const EXCHANGE_UPDATE_DEPOSIT_AMOUNT_FAILURE = - 'exchange/EXCHANGE_UPDATE_DEPOSIT_AMOUNT_FAILURE'; - -const EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_REQUEST = - 'exchange/EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_REQUEST'; -const EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_SUCCESS = - 'exchange/EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_SUCCESS'; -const EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_FAILURE = - 'exchange/EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_FAILURE'; - -const EXCHANGE_GET_WITHDRAWAL_PRICE_REQUEST = - 'exchange/EXCHANGE_GET_WITHDRAWAL_PRICE_REQUEST'; -const EXCHANGE_GET_WITHDRAWAL_PRICE_SUCCESS = - 'exchange/EXCHANGE_GET_WITHDRAWAL_PRICE_SUCCESS'; -const EXCHANGE_GET_WITHDRAWAL_PRICE_FAILURE = - 'exchange/EXCHANGE_GET_WITHDRAWAL_PRICE_FAILURE'; - -const EXCHANGE_UPDATE_WITHDRAWAL_NATIVE = - 'exchange/EXCHANGE_UPDATE_WITHDRAWAL_NATIVE'; - -const EXCHANGE_UPDATE_COUNTDOWN = 'exchange/EXCHANGE_UPDATE_COUNTDOWN'; - -const EXCHANGE_TOGGLE_CONFIRMATION_VIEW = - 'exchange/EXCHANGE_TOGGLE_CONFIRMATION_VIEW'; - -const EXCHANGE_TOGGLE_WITHDRAWAL_NATIVE = - 'exchange/EXCHANGE_TOGGLE_WITHDRAWAL_NATIVE'; - -const EXCHANGE_UPDATE_EXCHANGE_DETAILS = - 'exchange/EXCHANGE_UPDATE_EXCHANGE_DETAILS'; - -const EXCHANGE_UPDATE_MIN_MAX_LIMITS = - 'exchange/EXCHANGE_UPDATE_MIN_MAX_LIMITS'; - -const EXCHANGE_UPDATE_SELECTED = 'exchange/EXCHANGE_UPDATE_SELECTED'; - -const EXCHANGE_CLEAR_FIELDS = 'exchange/EXCHANGE_CLEAR_FIELDS'; - -// -- Actions --------------------------------------------------------------- // - -export const exchangeGetGasPrices = () => (dispatch, getState) => { - const { prices } = getState().account; - const { gasLimit } = getState().exchange; - dispatch({ type: EXCHANGE_GET_GAS_PRICE_REQUEST }); - apiGetGasPrices() - .then(({ data }) => { - console.log('get gas prices', data); - const gasPrices = parseGasPrices(data, prices, gasLimit); - console.log('parsed gas prices', gasPrices); - const gasPrice = gasPrices.fast; - dispatch({ type: EXCHANGE_GET_GAS_PRICE_SUCCESS, payload: gasPrice }); - }) - .catch(error => { - const message = parseError(error); - dispatch(notificationShow(message, true)); - dispatch({ type: EXCHANGE_GET_GAS_PRICE_FAILURE }); - }); -}; - -export const exchangeGetWithdrawalPrice = () => (dispatch, getState) => { - const { withdrawalSelected } = getState().exchange; - const { prices } = getState().account; - dispatch({ type: EXCHANGE_GET_WITHDRAWAL_PRICE_REQUEST }); - const nativeSelected = prices.selected.currency; - const withdrawalSymbol = withdrawalSelected.symbol; - return apiGetSinglePrice(withdrawalSymbol, nativeSelected) - .then(({ data }) => { - const amount = convertAmountToBigNumber(data[nativeSelected]); - const display = convertAmountToDisplay(amount, prices); - const withdrawalPrice = { amount, display }; - dispatch({ - type: EXCHANGE_GET_WITHDRAWAL_PRICE_SUCCESS, - payload: withdrawalPrice, - }); - return withdrawalPrice; - }) - .catch(error => { - const message = parseError(error); - dispatch(notificationShow(message, true)); - dispatch({ type: EXCHANGE_GET_WITHDRAWAL_PRICE_FAILURE }); - const { withdrawalPrice } = getState().exchange; - return withdrawalPrice; - }); -}; - -export const exchangeUpdateGasLimit = depositSelected => ( - dispatch, - getState, -) => { - const { address, recipient, depositAmount } = getState().exchange; - dispatch({ type: EXCHANGE_UPDATE_GAS_LIMIT_REQUEST }); - estimateGasLimit({ - asset: depositSelected, - address, - recipient, - amount: depositAmount, - }) - .then(gasLimit => { - console.log('>>>> gas limit', gasLimit); - dispatch({ - type: EXCHANGE_UPDATE_GAS_LIMIT_SUCCESS, - payload: gasLimit, - }); - }) - .catch(error => { - dispatch({ type: EXCHANGE_UPDATE_GAS_LIMIT_FAILURE }); - }); -}; - -export const exchangeUpdateMinMaxLimits = ( - depositSelected, - withdrawalSelected, -) => (dispatch, getState) => { - let { exchangeDetails } = getState().exchange; - const pair = `${depositSelected.symbol.toLowerCase()}_${withdrawalSelected.symbol.toLowerCase()}`; - return apiShapeshiftGetMarketInfo(pair) - .then(marketInfo => { - exchangeDetails.min = marketInfo.data.minimum; - exchangeDetails.maxLimit = marketInfo.data.maxLimit; - exchangeDetails.pair = marketInfo.data.pair.toLowerCase(); - exchangeDetails.quotedRate = marketInfo.data.rate; - exchangeDetails.minerFee = marketInfo.data.minerFee; - dispatch({ - type: EXCHANGE_UPDATE_MIN_MAX_LIMITS, - payload: { exchangeDetails }, - }); - return exchangeDetails; - }) - .catch(error => { - const message = parseError(error); - dispatch(notificationShow(message, true)); - dispatch({ - type: EXCHANGE_UPDATE_MIN_MAX_LIMITS, - payload: { exchangeDetails }, - }); - return exchangeDetails; - }); -}; - -export const exchangeUpdateDepositSelected = value => (dispatch, getState) => { - const { - withdrawalAssets, - depositAssets, - depositAmount, - withdrawalAmount, - withdrawalPrice, - priorityInput, - } = getState().exchange; - let { withdrawalSelected, depositSelected } = getState().exchange; - if (value === withdrawalSelected.symbol) { - withdrawalSelected = withdrawalAssets.filter( - asset => asset.symbol === depositSelected.symbol, - )[0]; - if (!withdrawalSelected) { - withdrawalSelected = withdrawalAssets.filter( - asset => asset.symbol !== value, - )[0]; - } - } - depositSelected = depositAssets.filter(asset => asset.symbol === 'ETH')[0]; - if (value !== 'ETH') { - depositSelected = depositAssets.filter(asset => asset.symbol === value)[0]; - } - - dispatch({ - type: EXCHANGE_UPDATE_SELECTED, - payload: { depositSelected, withdrawalSelected }, - }); - - dispatch(exchangeUpdateGasLimit(depositSelected)); - dispatch( - exchangeUpdateMinMaxLimits(depositSelected, withdrawalSelected), - ).then(exchangeDetails => { - if (priorityInput === 'DEPOSIT') { - dispatch( - exchangeUpdateDepositAmount( - depositAmount, - depositSelected, - withdrawalSelected, - exchangeDetails, - ), - ); - } else { - dispatch( - exchangeUpdateWithdrawalAmount( - withdrawalAmount, - depositSelected, - withdrawalSelected, - withdrawalPrice, - ), - ); - } - }); -}; - -export const exchangeUpdateWithdrawalSelected = value => ( - dispatch, - getState, -) => { - const { - withdrawalAssets, - depositAssets, - depositAmount, - withdrawalAmount, - priorityInput, - } = getState().exchange; - let { withdrawalSelected, depositSelected } = getState().exchange; - if (value === depositSelected.symbol) { - depositSelected = depositAssets.filter( - asset => asset.symbol === withdrawalSelected.symbol, - )[0]; - if (!depositSelected) { - depositSelected = depositAssets.filter( - asset => asset.symbol !== value, - )[0]; - } - } - withdrawalSelected = withdrawalAssets.filter( - asset => asset.symbol === 'ETH', - )[0]; - if (value !== 'ETH') { - withdrawalSelected = withdrawalAssets.filter( - asset => asset.symbol === value, - )[0]; - } - dispatch({ - type: EXCHANGE_UPDATE_SELECTED, - payload: { depositSelected, withdrawalSelected }, - }); - dispatch( - exchangeUpdateMinMaxLimits(depositSelected, withdrawalSelected), - ).then(exchangeDetails => { - dispatch(exchangeGetWithdrawalPrice()).then(withdrawalPrice => { - if (priorityInput === 'DEPOSIT') { - dispatch( - exchangeUpdateDepositAmount( - depositAmount, - depositSelected, - withdrawalSelected, - exchangeDetails, - ), - ); - } else { - dispatch( - exchangeUpdateWithdrawalAmount( - withdrawalAmount, - depositSelected, - withdrawalSelected, - withdrawalPrice, - ), - ); - } - }); - }); -}; - -export const exchangeUpdateDepositAmount = ( - depositAmount = '', - depositSelected = null, - withdrawalSelected = null, - exchangeDetails = null, -) => (dispatch, getState) => { - let { - withdrawalAmount, - withdrawalNative, - withdrawalPrice, - } = getState().exchange; - depositSelected = depositSelected || getState().exchange.depositSelected; - withdrawalSelected = - withdrawalSelected || getState().exchange.withdrawalSelected; - exchangeDetails = exchangeDetails || getState().exchange.exchangeDetails; - const parsedDepositAmount = parseFloat(depositAmount); - if ( - !parsedDepositAmount || - parsedDepositAmount < exchangeDetails.min || - parsedDepositAmount > exchangeDetails.maxLimit - ) { - withdrawalAmount = ''; - } - withdrawalNative = withdrawalAmount ? withdrawalNative : ''; - dispatch({ - type: EXCHANGE_UPDATE_DEPOSIT_AMOUNT_REQUEST, - payload: { depositAmount, withdrawalAmount, withdrawalNative }, - }); - if ( - parsedDepositAmount && - parsedDepositAmount >= exchangeDetails.min && - parsedDepositAmount <= exchangeDetails.maxLimit - ) { - apiShapeshiftGetExchangeDetails({ - request: { - depositSymbol: depositSelected.symbol, - withdrawalSymbol: withdrawalSelected.symbol, - depositAmount, - }, - inputOne: depositAmount, - inputTwo: withdrawalAmount, - withdrawal: false, - }) - .then(result => { - if (!result.exchangeDetails) { - result.exchangeDetails = getState().exchange.exchangeDetails; - } - const withdrawalNative = result.withdrawalAmount - ? multiply( - result.withdrawalAmount, - convertAmountFromBigNumber(withdrawalPrice.amount), - ) - : ''; - result.withdrawalNative = withdrawalNative; - dispatch({ - type: EXCHANGE_UPDATE_DEPOSIT_AMOUNT_SUCCESS, - payload: result, - }); - }) - .catch(error => { - const message = parseError(error); - dispatch(notificationShow(message, true)); - dispatch({ type: EXCHANGE_UPDATE_DEPOSIT_AMOUNT_FAILURE }); - }); - } else { - dispatch({ - type: EXCHANGE_UPDATE_DEPOSIT_AMOUNT_SUCCESS, - payload: { - exchangeDetails: getState().exchange.exchangeDetails, - withdrawalAmount: '', - }, - }); - } -}; - -export const exchangeUpdateWithdrawalAmount = ( - withdrawalAmount = '', - depositSelected = null, - withdrawalSelected = null, - withdrawalPrice = null, - disableNative = false, -) => (dispatch, getState) => { - let { depositAmount, withdrawalNative } = getState().exchange; - depositSelected = depositSelected || getState().exchange.depositSelected; - withdrawalSelected = - withdrawalSelected || getState().exchange.withdrawalSelected; - withdrawalPrice = withdrawalPrice || getState().exchange.withdrawalPrice; - - const parsedWithdrawalAmount = parseFloat(withdrawalAmount); - if (!parsedWithdrawalAmount || parsedWithdrawalAmount <= 0) { - depositAmount = ''; - } - withdrawalNative = disableNative - ? withdrawalNative - : withdrawalAmount - ? multiply( - withdrawalAmount, - convertAmountFromBigNumber(withdrawalPrice.amount), - ) - : ''; - dispatch({ - type: EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_REQUEST, - payload: { - fetchingRate: !!withdrawalAmount, - withdrawalAmount, - withdrawalNative, - depositAmount, - }, - }); - if (parsedWithdrawalAmount && parsedWithdrawalAmount > 0) { - apiShapeshiftGetExchangeDetails({ - request: { - depositSymbol: depositSelected.symbol, - withdrawalSymbol: withdrawalSelected.symbol, - withdrawalAmount, - }, - inputOne: withdrawalAmount, - inputTwo: depositAmount, - withdrawal: true, - }) - .then(result => { - if (!result.exchangeDetails) { - result.exchangeDetails = getState().exchange.exchangeDetails; - } - dispatch({ - type: EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_SUCCESS, - payload: result, - }); - }) - .catch(error => { - const message = parseError(error); - dispatch(notificationShow(message, true)); - dispatch({ type: EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_FAILURE }); - }); - } else { - dispatch({ - type: EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_SUCCESS, - payload: { - exchangeDetails: getState().exchange.exchangeDetails, - depositAmount: '', - }, - }); - } -}; - -export const exchangeUpdateWithdrawalNative = withdrawalNative => ( - dispatch, - getState, -) => { - const { - depositSelected, - withdrawalSelected, - withdrawalPrice, - } = getState().exchange; - const withdrawalAmount = divide( - withdrawalNative, - convertAmountFromBigNumber(withdrawalPrice.amount), - ); - dispatch({ - type: EXCHANGE_UPDATE_WITHDRAWAL_NATIVE, - payload: { withdrawalNative, withdrawalAmount }, - }); - dispatch( - exchangeUpdateWithdrawalAmount( - withdrawalAmount, - depositSelected, - withdrawalSelected, - withdrawalPrice, - true, - ), - ); -}; - -export const exchangeMaxBalance = () => (dispatch, getState) => { - const { depositSelected, gasPrice, exchangeDetails } = getState().exchange; - const { accountInfo } = getState().account; - let amount = ''; - if (depositSelected.symbol === 'ETH') { - const ethereum = accountInfo.assets.filter( - asset => asset.symbol === 'ETH', - )[0]; - const balanceAmount = ethereum.balance.amount; - const txFeeAmount = gasPrice.txFee.value.amount; - const remaining = convertStringToNumber( - subtract(balanceAmount, txFeeAmount), - ); - amount = convertAmountFromBigNumber(remaining < 0 ? '0' : remaining); - } else { - amount = convertAmountFromBigNumber(depositSelected.balance.amount); - } - if (greaterThan(amount, exchangeDetails.maxLimit)) { - amount = exchangeDetails.maxLimit; - } - dispatch(exchangeUpdateDepositAmount(amount)); -}; - -export const exchangeModalInit = () => (dispatch, getState) => { - const { - accountAddress, - accountInfo, - shapeshiftAvailable, - } = getState().account; - if (!shapeshiftAvailable) return; - const depositSelected = accountInfo.assets.filter( - asset => asset.symbol === 'ETH', - )[0]; - dispatch({ - type: EXCHANGE_GET_AVAILABLE_REQUEST, - payload: { address: accountAddress, depositSelected }, - }); - apiShapeshiftGetCurrencies() - .then(({ data }) => { - const withdrawalAssets = data; - const availableSymbols = withdrawalAssets.map( - availableAsset => availableAsset.symbol, - ); - const depositAssets = accountInfo.assets.filter( - asset => availableSymbols.indexOf(asset.symbol) !== -1, - ); - const withdrawalSelected = withdrawalAssets[0]; - dispatch({ - type: EXCHANGE_GET_AVAILABLE_SUCCESS, - payload: { - withdrawalAssets, - depositAssets, - withdrawalSelected, - }, - }); - - dispatch(exchangeGetGasPrices()); - dispatch(exchangeGetWithdrawalPrice()); - - apiShapeshiftSendAmount({ - depositSymbol: depositSelected.symbol, - withdrawalSymbol: withdrawalSelected.symbol, - withdrawalAmount: '0.5', - }) - .then(({ data }) => { - dispatch(exchangeUpdateExchangeDetails(data.success)); - }) - .catch(error => { - const message = parseError(error); - dispatch(notificationShow(message, true)); - }); - }) - .catch(error => { - const message = parseError(error); - dispatch(notificationShow(message, true)); - dispatch({ type: EXCHANGE_GET_AVAILABLE_FAILURE }); - }); -}; - -export const exchangeSendTransaction = () => (dispatch, getState) => { - const { - address, - recipient, - depositAmount, - depositSelected, - withdrawalAmount, - withdrawalSelected, - gasPrice, - gasLimit, - } = getState().exchange; - dispatch({ type: EXCHANGE_TRANSACTION_REQUEST }); - const transactionDetails = { - address, - recipient, - amount: depositAmount, - asset: depositSelected, - gasPrice, - gasLimit, - }; - dispatch(sendTransaction(transactionDetails, web3SendTransactionMultiWallet)) - .then(txHash => { - const incomingTx = { - hash: `shapeshift_${recipient}`, - asset: withdrawalSelected, - nonce: null, - from: '', - to: address, - amount: withdrawalAmount, - value: withdrawalAmount, - gasPrice: '', - gasLimit: '', - }; - dispatch(accountUpdateHasPendingTransaction()); - dispatch(accountUpdateTransactions(incomingTx)); - dispatch({ - type: EXCHANGE_TRANSACTION_SUCCESS, - payload: txHash, - }); - }) - .catch(error => { - const message = parseError(error); - dispatch(notificationShow(message, true)); - dispatch({ type: EXCHANGE_TRANSACTION_FAILURE }); - }); -}; - -export const exchangeToggleConfirmationView = () => ({ - type: EXCHANGE_TOGGLE_CONFIRMATION_VIEW, -}); - -let countdownTimeout = null; - -export const exchangeUpdateCountdown = () => (dispatch, getState) => { - const { confirm, exchangeDetails } = getState().exchange; - clearTimeout(countdownTimeout); - const countdown = subtract(exchangeDetails.expiration, Date.now()); - if (confirm) { - setTimeout(() => dispatch(exchangeUpdateCountdown()), 1000); // 1sec - if (greaterThanOrEqual(countdown, 1000)) { - dispatch({ type: EXCHANGE_UPDATE_COUNTDOWN, payload: countdown }); - } else { - dispatch(exchangeToggleConfirmationView()); - } - } -}; - -export const exchangeConfirmTransaction = request => (dispatch, getState) => { - const { - address, - priorityInput, - depositAmount, - withdrawalAmount, - depositSelected, - withdrawalSelected, - } = getState().exchange; - let request = { - address, - depositSymbol: depositSelected.symbol, - withdrawalSymbol: withdrawalSelected.symbol, - }; - if (priorityInput === 'DEPOSIT') { - request.depositAmount = depositAmount; - } else if (priorityInput === 'WITHDRAWAL') { - request.withdrawalAmount = withdrawalAmount; - } - dispatch({ type: EXCHANGE_CONFIRM_TRANSACTION_REQUEST }); - apiShapeshiftSendAmount(request) - .then(({ data }) => { - if (data.success) { - const exchangeDetails = data.success; - const recipient = exchangeDetails.deposit; - const withdrawalAmount = exchangeDetails.withdrawalAmount; - const depositAmount = exchangeDetails.depositAmount; - dispatch({ - type: EXCHANGE_CONFIRM_TRANSACTION_SUCCESS, - payload: { - exchangeDetails, - recipient, - withdrawalAmount, - depositAmount, - }, - }); - dispatch(exchangeSendTransaction()); - dispatch(exchangeUpdateCountdown()); - } - }) - .catch(error => { - dispatch({ type: EXCHANGE_CONFIRM_TRANSACTION_FAILURE }); - }); -}; - -export const exchangeUpdateExchangeDetails = exchangeDetails => ({ - type: EXCHANGE_UPDATE_EXCHANGE_DETAILS, - payload: exchangeDetails, -}); - -export const exchangeToggleWithdrawalNative = bool => (dispatch, getState) => { - let { - showWithdrawalNative, - withdrawalNative, - withdrawalAmount, - } = getState().exchange; - showWithdrawalNative = - typeof bool !== 'undefined' ? bool : !showWithdrawalNative; - const withdrawalInput = showWithdrawalNative - ? withdrawalNative - : withdrawalAmount; - dispatch({ - type: EXCHANGE_TOGGLE_WITHDRAWAL_NATIVE, - payload: { showWithdrawalNative, withdrawalInput }, - }); -}; - -export const exchangeClearFields = () => (dispatch, getState) => { - dispatch({ type: EXCHANGE_CLEAR_FIELDS }); -}; - -// -- Reducer --------------------------------------------------------------- // -const INITIAL_STATE = { - fetching: false, - fetchingRate: false, - fetchingFinal: false, - address: '', - recipient: '', - txHash: '', - confirm: false, - gasLimit: ethUnits.basic_tx, - gasPrice: {}, - countdown: '', - exchangeDetails: {}, - priorityInput: 'DEPOSIT', - depositAssets: [], - withdrawalAssets: [], - depositSelected: { symbol: 'ETH', decimals: 18 }, - withdrawalSelected: { symbol: 'ZRX', decimals: 18 }, - depositAmount: '', - withdrawalAmount: '', - withdrawalNative: '', - showWithdrawalNative: false, - withdrawalInput: '', - withdrawalPrice: { amount: '', display: '' }, -}; - -export default (state = INITIAL_STATE, action) => { - switch (action.type) { - case EXCHANGE_GET_AVAILABLE_REQUEST: - return { - ...state, - address: action.payload.address, - depositSelected: action.payload.depositSelected, - fetching: true, - }; - case EXCHANGE_GET_AVAILABLE_SUCCESS: - return { - ...state, - fetching: false, - withdrawalAssets: action.payload.withdrawalAssets, - depositAssets: action.payload.depositAssets, - withdrawalSelected: action.payload.withdrawalSelected, - }; - case EXCHANGE_GET_AVAILABLE_FAILURE: - return { - ...state, - fetching: false, - withdrawalAssets: [], - }; - case EXCHANGE_GET_GAS_PRICE_REQUEST: - return { - ...state, - fetching: true, - }; - case EXCHANGE_GET_GAS_PRICE_SUCCESS: - return { - ...state, - fetching: false, - gasPrice: action.payload, - }; - case EXCHANGE_GET_GAS_PRICE_FAILURE: - return { - ...state, - fetching: false, - }; - case EXCHANGE_GET_WITHDRAWAL_PRICE_SUCCESS: - return { - ...state, - withdrawalPrice: action.payload, - }; - case EXCHANGE_CONFIRM_TRANSACTION_REQUEST: - return { - ...state, - fetching: true, - }; - case EXCHANGE_CONFIRM_TRANSACTION_SUCCESS: - return { - ...state, - confirm: true, - fetching: false, - exchangeDetails: action.payload.exchangeDetails, - recipient: action.payload.recipient, - withdrawalAmount: action.payload.withdrawalAmount, - depositAmount: action.payload.depositAmount, - }; - case EXCHANGE_CONFIRM_TRANSACTION_FAILURE: - return { - ...state, - fetching: false, - confirm: false, - }; - case EXCHANGE_TRANSACTION_SUCCESS: - return { - ...state, - txHash: action.payload, - }; - case EXCHANGE_TRANSACTION_FAILURE: - return { - ...state, - fetching: false, - txHash: '', - confirm: false, - }; - case EXCHANGE_UPDATE_DEPOSIT_AMOUNT_REQUEST: - return { - ...state, - priorityInput: 'DEPOSIT', - fetchingRate: true, - depositAmount: action.payload.depositAmount, - withdrawalAmount: action.payload.withdrawalAmount, - withdrawalNative: action.payload.withdrawalNative, - withdrawalInput: state.showWithdrawalNative - ? action.payload.withdrawalNative - : action.payload.withdrawalAmount, - }; - case EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_REQUEST: - return { - ...state, - priorityInput: 'WITHDRAWAL', - fetchingRate: action.payload.fetchingRate, - depositAmount: action.payload.depositAmount, - withdrawalAmount: action.payload.withdrawalAmount, - withdrawalNative: action.payload.withdrawalNative, - withdrawalInput: state.showWithdrawalNative - ? action.payload.withdrawalNative - : action.payload.withdrawalAmount, - }; - case EXCHANGE_UPDATE_DEPOSIT_AMOUNT_SUCCESS: - return { - ...state, - fetchingRate: false, - exchangeDetails: action.payload.exchangeDetails, - withdrawalAmount: action.payload.withdrawalAmount, - withdrawalNative: action.payload.withdrawalNative, - withdrawalInput: state.showWithdrawalNative - ? action.payload.withdrawalNative - : action.payload.withdrawalAmount, - }; - case EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_SUCCESS: - return { - ...state, - fetchingRate: false, - exchangeDetails: action.payload.exchangeDetails, - depositAmount: action.payload.depositAmount, - }; - case EXCHANGE_UPDATE_DEPOSIT_AMOUNT_FAILURE: - return { ...state, fetchingRate: false }; - case EXCHANGE_UPDATE_WITHDRAWAL_AMOUNT_FAILURE: - return { ...state, fetchingRate: false }; - case EXCHANGE_UPDATE_GAS_LIMIT_SUCCESS: - return { - ...state, - gasLimit: action.payload, - }; - case EXCHANGE_UPDATE_SELECTED: - return { - ...state, - depositSelected: action.payload.depositSelected, - withdrawalSelected: action.payload.withdrawalSelected, - }; - case EXCHANGE_UPDATE_MIN_MAX_LIMITS: - return { - ...state, - exchangeDetails: action.payload.exchangeDetails, - }; - case EXCHANGE_UPDATE_EXCHANGE_DETAILS: - return { - ...state, - exchangeDetails: action.payload, - }; - case EXCHANGE_UPDATE_COUNTDOWN: - return { ...state, countdown: action.payload }; - case EXCHANGE_TOGGLE_CONFIRMATION_VIEW: - return { ...state, countdown: '', confirm: !state.confirm }; - case EXCHANGE_UPDATE_WITHDRAWAL_NATIVE: - return { - ...state, - withdrawalAmount: action.payload.withdrawalAmount, - withdrawalNative: action.payload.withdrawalNative, - withdrawalInput: state.showWithdrawalNative - ? action.payload.withdrawalNative - : action.payload.withdrawalAmount, - }; - case EXCHANGE_TOGGLE_WITHDRAWAL_NATIVE: - return { - ...state, - showWithdrawalNative: action.payload.showWithdrawalNative, - withdrawalInput: action.payload.withdrawalInput, - }; - case EXCHANGE_CLEAR_FIELDS: - return { ...state, ...INITIAL_STATE }; - default: - return state; - } -}; diff --git a/src/reducers/index.js b/src/reducers/index.js index 6803e9be..c4fa2277 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -1,6 +1,5 @@ import { combineReducers } from 'redux'; import account from './_account'; -import exchange from './_exchange'; import modal from './_modal'; import ledger from './_ledger'; import send from './_send'; @@ -12,7 +11,6 @@ import warning from './_warning'; import zrxinstant from './_zrxinstant'; export default combineReducers({ - exchange, send, account, modal, diff --git a/yarn.lock b/yarn.lock index c3f351e5..44dcc4f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2094,37 +2094,6 @@ backoff@^2.5.0: dependencies: precond "0.2" -balance-common@../balance-common/: - version "0.6.1" - dependencies: - axios "^0.18.0" - bignumber.js "^7.0.1" - date-fns "^1.29.0" - i18next "^11.3.2" - jsonp "^0.2.1" - lodash "^4.17.10" - react "^16.2.0" - react-redux "^5.0.7" - redux "^4.0.0" - underscore "^1.9.1" - web3 "1.0.0-beta.34" - -balance-common@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/balance-common/-/balance-common-0.6.1.tgz#8953a6d3ad83dde0d736b7128e042c454e92fb9e" - dependencies: - axios "^0.18.0" - bignumber.js "^7.0.1" - date-fns "^1.29.0" - i18next "^11.3.2" - jsonp "^0.2.1" - lodash "^4.17.10" - react "^16.2.0" - react-redux "^5.0.7" - redux "^4.0.0" - underscore "^1.9.1" - web3 "1.0.0-beta.34" - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -3374,7 +3343,7 @@ data-urls@^1.0.0, data-urls@^1.0.1: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -date-fns@^1.27.2, date-fns@^1.29.0: +date-fns@^1.27.2: version "1.29.0" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6" @@ -11007,10 +10976,6 @@ underscore@1.8.3: version "1.8.3" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" -underscore@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" - unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" @@ -11296,14 +11261,6 @@ wbuf@^1.1.0, wbuf@^1.7.2: dependencies: minimalistic-assert "^1.0.0" -web3-bzz@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.0.0-beta.34.tgz#068d37777ab65e5c60f8ec8b9a50cfe45277929c" - dependencies: - got "7.1.0" - swarm-js "0.1.37" - underscore "1.8.3" - web3-bzz@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.0.0-beta.35.tgz#9d5e1362b3db2afd77d65619b7cd46dd5845c192" @@ -11320,14 +11277,6 @@ web3-bzz@1.0.0-beta.36: swarm-js "0.1.37" underscore "1.8.3" -web3-core-helpers@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.34.tgz#b168da00d3e19e156bc15ae203203dd4dfee2d03" - dependencies: - underscore "1.8.3" - web3-eth-iban "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3-core-helpers@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.35.tgz#d681d218a0c6e3283ee1f99a078ab9d3eef037f1" @@ -11344,16 +11293,6 @@ web3-core-helpers@1.0.0-beta.36: web3-eth-iban "1.0.0-beta.36" web3-utils "1.0.0-beta.36" -web3-core-method@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.34.tgz#ec163c8a2c490fa02a7ec15559fa7307fc7cc6dd" - dependencies: - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-core-promievent "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3-core-method@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.35.tgz#fc10e2d546cf4886038e6130bd5726b0952a4e5f" @@ -11374,13 +11313,6 @@ web3-core-method@1.0.0-beta.36: web3-core-subscriptions "1.0.0-beta.36" web3-utils "1.0.0-beta.36" -web3-core-promievent@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.34.tgz#a4f4fa6784bb293e82c60960ae5b56a94cd03edc" - dependencies: - any-promise "1.3.0" - eventemitter3 "1.1.1" - web3-core-promievent@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.0.0-beta.35.tgz#4f1b24737520fa423fee3afee110fbe82bcb8691" @@ -11395,16 +11327,6 @@ web3-core-promievent@1.0.0-beta.36: any-promise "1.3.0" eventemitter3 "1.1.1" -web3-core-requestmanager@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.34.tgz#01f8f6cf2ae6b6f0b70c38bae1ef741b5bab215c" - dependencies: - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-providers-http "1.0.0-beta.34" - web3-providers-ipc "1.0.0-beta.34" - web3-providers-ws "1.0.0-beta.34" - web3-core-requestmanager@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.0.0-beta.35.tgz#2b77cbf6303720ad68899b39fa7f584dc03dbc8f" @@ -11425,14 +11347,6 @@ web3-core-requestmanager@1.0.0-beta.36: web3-providers-ipc "1.0.0-beta.36" web3-providers-ws "1.0.0-beta.36" -web3-core-subscriptions@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.34.tgz#9fed144033f221c3cf21060302ffdaf5ef2de2de" - dependencies: - eventemitter3 "1.1.1" - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-core-subscriptions@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.35.tgz#c1b76a2ad3c6e80f5d40b8ba560f01e0f4628758" @@ -11449,15 +11363,6 @@ web3-core-subscriptions@1.0.0-beta.36: underscore "1.8.3" web3-core-helpers "1.0.0-beta.36" -web3-core@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.34.tgz#121be8555e9fb00d2c5d05ddd3381d0c9e46987e" - dependencies: - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-requestmanager "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3-core@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.35.tgz#0c44d3c50d23219b0b1531d145607a9bc7cd4b4f" @@ -11476,15 +11381,6 @@ web3-core@1.0.0-beta.36: web3-core-requestmanager "1.0.0-beta.36" web3-utils "1.0.0-beta.36" -web3-eth-abi@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.34.tgz#034533e3aa2f7e59ff31793eaea685c0ed5af67a" - dependencies: - bn.js "4.11.6" - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3-eth-abi@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.35.tgz#2eb9c1c7c7233db04010defcb192293e0db250e6" @@ -11502,21 +11398,6 @@ web3-eth-abi@1.0.0-beta.36: underscore "1.8.3" web3-utils "1.0.0-beta.36" -web3-eth-accounts@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.34.tgz#e09142eeecc797ac3459b75e9b23946d3695f333" - dependencies: - any-promise "1.3.0" - crypto-browserify "3.12.0" - eth-lib "0.2.7" - scrypt.js "0.2.0" - underscore "1.8.3" - uuid "2.0.1" - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3-eth-accounts@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.35.tgz#7d0e5a69f510dc93874471599eb7abfa9ddf3e63" @@ -11547,19 +11428,6 @@ web3-eth-accounts@1.0.0-beta.36: web3-core-method "1.0.0-beta.36" web3-utils "1.0.0-beta.36" -web3-eth-contract@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.34.tgz#9dbb38fae7643a808427a20180470ec7415c91e6" - dependencies: - underscore "1.8.3" - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-promievent "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-eth-abi "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3-eth-contract@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.35.tgz#5276242d8a3358d9f1ce92b71575c74f9015935c" @@ -11599,13 +11467,6 @@ web3-eth-ens@1.0.0-beta.36: web3-eth-contract "1.0.0-beta.36" web3-utils "1.0.0-beta.36" -web3-eth-iban@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.34.tgz#9af458605867ccf74ea979aaf326b38ba6a5ba0c" - dependencies: - bn.js "4.11.6" - web3-utils "1.0.0-beta.34" - web3-eth-iban@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.35.tgz#5aa10327a9abb26bcfc4ba79d7bad18a002b332c" @@ -11620,16 +11481,6 @@ web3-eth-iban@1.0.0-beta.36: bn.js "4.11.6" web3-utils "1.0.0-beta.36" -web3-eth-personal@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.34.tgz#9afba167342ebde5420bcd5895c3f6c34388f205" - dependencies: - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3-eth-personal@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.35.tgz#ecac95b7a53d04a567447062d5cae5f49879e89f" @@ -11650,23 +11501,6 @@ web3-eth-personal@1.0.0-beta.36: web3-net "1.0.0-beta.36" web3-utils "1.0.0-beta.36" -web3-eth@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.34.tgz#74086000850c6fe6f535ef49837d6d4bb6113268" - dependencies: - underscore "1.8.3" - web3-core "1.0.0-beta.34" - web3-core-helpers "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-eth-abi "1.0.0-beta.34" - web3-eth-accounts "1.0.0-beta.34" - web3-eth-contract "1.0.0-beta.34" - web3-eth-iban "1.0.0-beta.34" - web3-eth-personal "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3-eth@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.35.tgz#c52c804afb95e6624b6f5e72a9af90fbf5005b68" @@ -11702,14 +11536,6 @@ web3-eth@1.0.0-beta.36: web3-net "1.0.0-beta.36" web3-utils "1.0.0-beta.36" -web3-net@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.34.tgz#427cea2f431881449c8e38d523290f173f9ff63d" - dependencies: - web3-core "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3-net@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.35.tgz#5c6688e0dea71fcd910ee9dc5437b94b7f6b3354" @@ -11801,13 +11627,6 @@ web3-provider-engine@^13.3.2: xhr "^2.2.0" xtend "^4.0.1" -web3-providers-http@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.34.tgz#e561b52bbb43766282007d40285bfe3550c27e7a" - dependencies: - web3-core-helpers "1.0.0-beta.34" - xhr2 "0.1.4" - web3-providers-http@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.0.0-beta.35.tgz#92059d9d6de6e9f82f4fae30b743efd841afc1e1" @@ -11822,14 +11641,6 @@ web3-providers-http@1.0.0-beta.36: web3-core-helpers "1.0.0-beta.36" xhr2-cookies "1.1.0" -web3-providers-ipc@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.34.tgz#a1b77f1a306d73649a9c039052e40cb71328d00a" - dependencies: - oboe "2.1.3" - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - web3-providers-ipc@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.0.0-beta.35.tgz#031afeb10fade2ebb0ef2fb82f5e58c04be842d9" @@ -11846,14 +11657,6 @@ web3-providers-ipc@1.0.0-beta.36: underscore "1.8.3" web3-core-helpers "1.0.0-beta.36" -web3-providers-ws@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.34.tgz#7de70f1b83f2de36476772156becfef6e3516eb3" - dependencies: - underscore "1.8.3" - web3-core-helpers "1.0.0-beta.34" - websocket "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible" - web3-providers-ws@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.0.0-beta.35.tgz#5d38603fd450243a26aae0ff7f680644e77fa240" @@ -11870,15 +11673,6 @@ web3-providers-ws@1.0.0-beta.36: web3-core-helpers "1.0.0-beta.36" websocket "git://github.com/frozeman/WebSocket-Node.git#browserifyCompatible" -web3-shh@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.34.tgz#975061d71eaec42ccee576f7bd8f70f03844afe0" - dependencies: - web3-core "1.0.0-beta.34" - web3-core-method "1.0.0-beta.34" - web3-core-subscriptions "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-shh@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.35.tgz#7e4a585f8beee0c1927390937c6537748a5d1a58" @@ -11897,18 +11691,6 @@ web3-shh@1.0.0-beta.36: web3-core-subscriptions "1.0.0-beta.36" web3-net "1.0.0-beta.36" -web3-utils@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.34.tgz#9411fc39aaef39ca4e06169f762297d9ff020970" - dependencies: - bn.js "4.11.6" - eth-lib "0.1.27" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randomhex "0.1.5" - underscore "1.8.3" - utf8 "2.1.1" - web3-utils@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.35.tgz#ced9e1df47c65581c441c5f2af76b05a37a273d7" @@ -11943,18 +11725,6 @@ web3@0.20.2: xhr2 "*" xmlhttprequest "*" -web3@1.0.0-beta.34: - version "1.0.0-beta.34" - resolved "http://registry.npmjs.org/web3/-/web3-1.0.0-beta.34.tgz#347e561b784098cb5563315f490479a1d91f2ab1" - dependencies: - web3-bzz "1.0.0-beta.34" - web3-core "1.0.0-beta.34" - web3-eth "1.0.0-beta.34" - web3-eth-personal "1.0.0-beta.34" - web3-net "1.0.0-beta.34" - web3-shh "1.0.0-beta.34" - web3-utils "1.0.0-beta.34" - web3@1.0.0-beta.35: version "1.0.0-beta.35" resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.35.tgz#6475095bd451a96e50a32b997ddee82279292f11" @@ -12380,7 +12150,7 @@ xhr2-cookies@1.1.0: dependencies: cookiejar "^2.1.1" -xhr2@*, xhr2@0.1.4: +xhr2@*: version "0.1.4" resolved "https://registry.yarnpkg.com/xhr2/-/xhr2-0.1.4.tgz#7f87658847716db5026323812f818cadab387a5f" From 9987ac78339184b00acfd3e2ae7dff0e8f3cbd66 Mon Sep 17 00:00:00 2001 From: Jack Leslie Date: Mon, 3 Jun 2019 00:22:09 +0100 Subject: [PATCH 4/4] Remove eslint warnings from common --- src/handlers/api.js | 1 - src/handlers/commonStorage.js | 1 - src/reducers/_account.js | 4 ---- 3 files changed, 6 deletions(-) diff --git a/src/handlers/api.js b/src/handlers/api.js index 86b64ee8..7c48a697 100644 --- a/src/handlers/api.js +++ b/src/handlers/api.js @@ -5,7 +5,6 @@ import { parseAccountTransactions, parseHistoricalTransactions, } from './parsers'; -import { formatInputDecimals } from '../helpers/bignumber'; import nativeCurrencies from '../references/native-currencies.json'; /** diff --git a/src/handlers/commonStorage.js b/src/handlers/commonStorage.js index 535944fa..5bc0f039 100644 --- a/src/handlers/commonStorage.js +++ b/src/handlers/commonStorage.js @@ -1,4 +1,3 @@ -import { differenceInMinutes } from 'date-fns'; import { omit, pickBy } from 'lodash'; const defaultVersion = '0.1.0'; diff --git a/src/reducers/_account.js b/src/reducers/_account.js index cdf940fd..448f14af 100644 --- a/src/reducers/_account.js +++ b/src/reducers/_account.js @@ -39,12 +39,8 @@ const ACCOUNT_GET_ACCOUNT_TRANSACTIONS_NO_NEW_PAYLOAD_SUCCESS = const ACCOUNT_GET_ACCOUNT_TRANSACTIONS_FAILURE = 'account/ACCOUNT_GET_ACCOUNT_TRANSACTIONS_FAILURE'; -const ACCOUNT_CHECK_TRANSACTION_STATUS_REQUEST = - 'account/ACCOUNT_CHECK_TRANSACTION_STATUS_REQUEST'; const ACCOUNT_CHECK_TRANSACTION_STATUS_SUCCESS = 'account/ACCOUNT_CHECK_TRANSACTION_STATUS_SUCCESS'; -const ACCOUNT_CHECK_TRANSACTION_STATUS_FAILURE = - 'account/ACCOUNT_CHECK_TRANSACTION_STATUS_FAILURE'; const ACCOUNT_UPDATE_TRANSACTIONS_REQUEST = 'account/ACCOUNT_UPDATE_TRANSACTIONS_REQUEST';