Configured Prettier (#60)

* Apply prettier fixes for UI

* Integrate prettier into linting. Remove duplicate .prettierrc.

* Common .eslintignore. Removed reduntant prettier check

* Apply prettier on ui script

* Correct linting

* Duplicate plugin

* Common plugin
This commit is contained in:
Przemyslaw Rzad 2019-05-22 16:31:09 +02:00 committed by GitHub
parent 19a3314c42
commit 6dc4fc6acd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
91 changed files with 1544 additions and 1243 deletions

View File

@ -1,3 +1,4 @@
node_modules
submodules
coverage
lib

22
.eslintrc Normal file
View File

@ -0,0 +1,22 @@
{
"extends": [
"plugin:prettier/recommended"
],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"arrow-body-style": "off",
"func-names": "off",
"no-await-in-loop": "off",
"no-console": "off",
"no-else-return": "off",
"no-param-reassign": "off",
"no-plusplus": "off",
"no-restricted-syntax": "off",
"no-shadow": "off",
"no-use-before-define": ["error", { "functions": false }],
"import/no-dynamic-require": "off",
"prefer-template": "off",
"no-underscore-dangle": "off"
}
}

View File

@ -1,21 +1,11 @@
{
"plugins": ["node", "prettier"],
"plugins": ["node"],
"extends": [
"plugin:node/recommended",
"airbnb-base",
"plugin:prettier/recommended"
"../.eslintrc"
],
"rules": {
"prettier/prettier": "error",
"arrow-body-style": "off",
"no-await-in-loop": "off",
"no-console": "off",
"no-else-return": "off",
"no-plusplus": "off",
"no-restricted-syntax": "off",
"no-shadow": "off",
"prefer-template": "off",
"no-use-before-define": "off",
"no-underscore-dangle": "off"
"no-use-before-define": "off"
}
}

View File

@ -5,7 +5,7 @@
"main": "index.js",
"scripts": {
"start": "node index.js",
"lint": "eslint .",
"lint": "eslint . --ignore-path ../.eslintignore",
"lint:fix": "eslint . --fix"
},
"author": "",

View File

@ -1,7 +0,0 @@
{
"extends": "../.eslintrc",
"globals": {
"describe": false,
"it": false
}
}

View File

@ -2,20 +2,8 @@
"extends": [
"plugin:node/recommended",
"airbnb-base",
"plugin:prettier/recommended"
"../.eslintrc"
],
"plugins": ["node"],
"rules": {
"arrow-body-style": "off",
"func-names": "off",
"no-await-in-loop": "off",
"no-console": "off",
"no-else-return": "off",
"no-param-reassign": "off",
"no-plusplus": "off",
"no-restricted-syntax": "off",
"no-shadow": "off",
"no-use-before-define": ["error", { "functions": false }],
"import/no-dynamic-require": "off"
}
"rules": {}
}

View File

@ -1,5 +0,0 @@
{
"semi": false,
"singleQuote": true,
"printWidth": 100
}

View File

@ -4,7 +4,7 @@
"description": "",
"main": "index.js",
"scripts": {
"lint": "eslint .",
"lint": "eslint . --ignore-path ../.eslintignore",
"watcher:signature-request": "./scripts/start-worker.sh watcher signature-request-watcher",
"watcher:collected-signatures": "./scripts/start-worker.sh watcher collected-signatures-watcher",
"watcher:affirmation-request": "./scripts/start-worker.sh watcher affirmation-request-watcher",

View File

@ -1,4 +0,0 @@
node_modules
submodules
coverage
e2e-script

View File

@ -1,5 +1,11 @@
{
"extends": "react-app",
"extends": [
"react-app",
"../.eslintrc"
],
"rules": {
"no-use-before-define": "off"
},
"parserOptions": {
"ecmaFeatures": {
"legacyDecorators": true

View File

@ -1,10 +1,3 @@
const {
addDecoratorsLegacy,
disableEsLint,
override
} = require("customize-cra");
const { addDecoratorsLegacy, disableEsLint, override } = require('customize-cra')
module.exports = override(
addDecoratorsLegacy(),
disableEsLint()
);
module.exports = override(addDecoratorsLegacy(), disableEsLint())

View File

@ -32,7 +32,7 @@
"web3-utils": "1.0.0-beta.30"
},
"scripts": {
"lint": "eslint .",
"lint": "eslint . --ignore-path ../.eslintignore",
"select-css-theme": "node scripts/selectTheme.js",
"build-css": "node-sass-chokidar src/assets/stylesheets -o src/assets/stylesheets --output-style=compressed -m application*.css",
"watch-css": "nodemon -e scss -x \"npm run build-css\"",

View File

@ -1,17 +1,17 @@
const path = require('path');
const path = require('path')
require('dotenv').config({
path: path.resolve(__dirname, '..', '.env')
});
const fs = require('fs');
path: path.resolve(__dirname, '..', '.env')
})
const fs = require('fs')
const stylePath = path.resolve(__dirname, '..', 'src', 'assets', 'stylesheets');
const destinationFilename = 'application.css';
let filename;
const stylePath = path.resolve(__dirname, '..', 'src', 'assets', 'stylesheets')
const destinationFilename = 'application.css'
let filename
if (process.env.APP_STYLES === 'classic') {
filename = 'application.classic.css'
filename = 'application.classic.css'
} else {
filename = 'application.core.css'
filename = 'application.core.css'
}
fs.copyFileSync(path.resolve(stylePath, filename), path.resolve(stylePath, destinationFilename));
fs.copyFileSync(path.resolve(stylePath, filename), path.resolve(stylePath, destinationFilename))

View File

@ -1,7 +1,16 @@
import React from 'react';
import { Header, Bridge, RelayEvents, Footer, SweetAlert, Loading, StatusPage, StatisticsPage } from './components';
import React from 'react'
import {
Header,
Bridge,
RelayEvents,
Footer,
SweetAlert,
Loading,
StatusPage,
StatisticsPage
} from './components'
import { Route } from 'react-router-dom'
import './assets/stylesheets/application.css';
import './assets/stylesheets/application.css'
import { Disclaimer } from './components'
import { ModalContainer } from './components'
import { NoWallet } from './components'
@ -16,43 +25,40 @@ export class App extends React.Component {
componentDidMount() {
const disclaimerDisplayed = getItem(DISCLAIMER_KEY)
if(!disclaimerDisplayed) {
if (!disclaimerDisplayed) {
this.setState({ showDisclaimer: true })
}
}
closeDisclaimer = () => {
setItem(DISCLAIMER_KEY, true)
this.setState({showDisclaimer: false})
this.setState({ showDisclaimer: false })
}
toggleMobileMenu = () => {
this.setState(prevState => ({ showMobileMenu: !prevState.showMobileMenu}))
this.setState(prevState => ({ showMobileMenu: !prevState.showMobileMenu }))
}
render() {
const { showDisclaimer, showMobileMenu } = this.state
return (
<div className={showMobileMenu ? 'mobile-menu-is-open' : ''}>
<Route component={Loading}/>
<Route component={SweetAlert}/>
<Route render={() =>
<Header
onMenuToggle={this.toggleMobileMenu}
showMobileMenu={showMobileMenu}
/>
}/>
<Route component={Loading} />
<Route component={SweetAlert} />
<Route
render={() => (
<Header onMenuToggle={this.toggleMobileMenu} showMobileMenu={showMobileMenu} />
)}
/>
<div className="app-container">
{showMobileMenu && <Route render={() => <div className="mobile-menu-open"/>}/>}
<Route exact path="/" component={Bridge}/>
<Route exact path="/events" component={RelayEvents}/>
<Route exact path="/status" component={StatusPage}/>
<Route exact path="/statistics" component={StatisticsPage}/>
{showMobileMenu && <Route render={() => <div className="mobile-menu-open" />} />}
<Route exact path="/" component={Bridge} />
<Route exact path="/events" component={RelayEvents} />
<Route exact path="/status" component={StatusPage} />
<Route exact path="/statistics" component={StatisticsPage} />
</div>
<Route component={Footer}/>
<ModalContainer
showModal={showDisclaimer}
>
<Route component={Footer} />
<ModalContainer showModal={showDisclaimer}>
<Disclaimer onConfirmation={this.closeDisclaimer} />
</ModalContainer>
<NoWallet showModal={!showDisclaimer} />

View File

@ -2,9 +2,9 @@ import React from 'react'
export const Authority = ({ address, number, logoIndex }) => (
<div className="authority">
<span className='authority-number'>{number}</span>
<span className="authority-number">{number}</span>
<div className="separator" />
<div className={`authority-logo authority-logo-${logoIndex}`} />
<span className='authority-address'>{address}</span>
<span className="authority-address">{address}</span>
</div>
);
)

View File

@ -1,5 +1,5 @@
import BN from 'bignumber.js'
import React from 'react';
import React from 'react'
import { toHex } from 'web3-utils'
import foreignLogoPurple from '../assets/images/logos/logo-poa-20-purple@2x.png'
import homeLogoPurple from '../assets/images/logos/logo-poa-sokol-purple@2x.png'
@ -12,15 +12,15 @@ import { ModalContainer } from './ModalContainer'
import { NetworkDetails } from './NetworkDetails'
import { TransferAlert } from './TransferAlert'
import { getFeeToApply, validFee } from '../stores/utils/rewardable'
import { inject, observer } from "mobx-react";
import { inject, observer } from 'mobx-react'
import { toDecimals } from '../stores/utils/decimals'
@inject("RootStore")
@inject('RootStore')
@observer
export class Bridge extends React.Component {
state = {
reverse: false,
amount:'',
amount: '',
modalData: {},
confirmationData: {},
showModal: false,
@ -36,7 +36,7 @@ export class Bridge extends React.Component {
componentDidMount() {
const { web3Store } = this.props.RootStore
web3Store.getWeb3Promise.then(() => {
if(!web3Store.metamaskNet.id || !web3Store.foreignNet.id) {
if (!web3Store.metamaskNet.id || !web3Store.foreignNet.id) {
this.forceUpdate()
} else {
const reverse = web3Store.metamaskNet.id.toString() === web3Store.foreignNet.id.toString()
@ -61,28 +61,40 @@ export class Bridge extends React.Component {
})
}
async _sendToHome(amount){
async _sendToHome(amount) {
const { web3Store, homeStore, alertStore, txStore, bridgeMode } = this.props.RootStore
const isErcToErcMode = bridgeMode === BRIDGE_MODES.ERC_TO_ERC
const { isLessThan, isGreaterThan } = this
if(web3Store.metamaskNet.id.toString() !== web3Store.homeNet.id.toString()){
swal("Error", `Please switch wallet to ${web3Store.homeNet.name} network`, "error")
if (web3Store.metamaskNet.id.toString() !== web3Store.homeNet.id.toString()) {
swal('Error', `Please switch wallet to ${web3Store.homeNet.name} network`, 'error')
return
}
if(isLessThan(amount, homeStore.minPerTx)){
alertStore.pushError(`The amount is less than current minimum per transaction amount.\nThe minimum per transaction amount is: ${homeStore.minPerTx} ${homeStore.symbol}`)
if (isLessThan(amount, homeStore.minPerTx)) {
alertStore.pushError(
`The amount is less than current minimum per transaction amount.\nThe minimum per transaction amount is: ${
homeStore.minPerTx
} ${homeStore.symbol}`
)
return
}
if(isGreaterThan(amount, homeStore.maxPerTx)){
alertStore.pushError(`The amount is above current maximum per transaction limit.\nThe maximum per transaction limit is: ${homeStore.maxPerTx} ${homeStore.symbol}`)
if (isGreaterThan(amount, homeStore.maxPerTx)) {
alertStore.pushError(
`The amount is above current maximum per transaction limit.\nThe maximum per transaction limit is: ${
homeStore.maxPerTx
} ${homeStore.symbol}`
)
return
}
if(isGreaterThan(amount, homeStore.maxCurrentDeposit)){
alertStore.pushError(`The amount is above current daily limit.\nThe max deposit today: ${homeStore.maxCurrentDeposit} ${homeStore.symbol}`)
if (isGreaterThan(amount, homeStore.maxCurrentDeposit)) {
alertStore.pushError(
`The amount is above current daily limit.\nThe max deposit today: ${
homeStore.maxCurrentDeposit
} ${homeStore.symbol}`
)
return
}
if(isGreaterThan(amount, homeStore.getDisplayedBalance())){
alertStore.pushError("Insufficient balance")
if (isGreaterThan(amount, homeStore.getDisplayedBalance())) {
alertStore.pushError('Insufficient balance')
} else {
try {
alertStore.setLoading(true)
@ -90,12 +102,12 @@ export class Bridge extends React.Component {
return txStore.erc677transferAndCall({
to: homeStore.HOME_BRIDGE_ADDRESS,
from: web3Store.defaultAccount.address,
value: toDecimals(amount,homeStore.tokenDecimals),
value: toDecimals(amount, homeStore.tokenDecimals),
contract: homeStore.tokenContract,
tokenAddress: homeStore.tokenAddress
})
} else {
const value = toHex(toDecimals(amount,homeStore.tokenDecimals))
const value = toHex(toDecimals(amount, homeStore.tokenDecimals))
return txStore.doSend({
to: homeStore.HOME_BRIDGE_ADDRESS,
from: web3Store.defaultAccount.address,
@ -110,28 +122,42 @@ export class Bridge extends React.Component {
}
}
async _sendToForeign(amount){
async _sendToForeign(amount) {
const { web3Store, foreignStore, alertStore, txStore } = this.props.RootStore
const isExternalErc20 = foreignStore.tokenType === ERC_TYPES.ERC20
const { isLessThan, isGreaterThan } = this
if(web3Store.metamaskNet.id.toString() !== web3Store.foreignNet.id.toString()){
swal("Error", `Please switch wallet to ${web3Store.foreignNet.name} network`, "error")
if (web3Store.metamaskNet.id.toString() !== web3Store.foreignNet.id.toString()) {
swal('Error', `Please switch wallet to ${web3Store.foreignNet.name} network`, 'error')
return
}
if(!isExternalErc20 && isLessThan(amount, foreignStore.minPerTx)){
alertStore.pushError(`The amount is less than minimum amount per transaction.\nThe min per transaction is: ${foreignStore.minPerTx} ${foreignStore.symbol}`)
if (!isExternalErc20 && isLessThan(amount, foreignStore.minPerTx)) {
alertStore.pushError(
`The amount is less than minimum amount per transaction.\nThe min per transaction is: ${
foreignStore.minPerTx
} ${foreignStore.symbol}`
)
return
}
if(!isExternalErc20 && isGreaterThan(amount, foreignStore.maxPerTx)){
alertStore.pushError(`The amount is above maximum amount per transaction.\nThe max per transaction is: ${foreignStore.maxPerTx} ${foreignStore.symbol}`)
if (!isExternalErc20 && isGreaterThan(amount, foreignStore.maxPerTx)) {
alertStore.pushError(
`The amount is above maximum amount per transaction.\nThe max per transaction is: ${
foreignStore.maxPerTx
} ${foreignStore.symbol}`
)
return
}
if(!isExternalErc20 && isGreaterThan(amount, foreignStore.maxCurrentDeposit)){
alertStore.pushError(`The amount is above current daily limit.\nThe max withdrawal today: ${foreignStore.maxCurrentDeposit} ${foreignStore.symbol}`)
if (!isExternalErc20 && isGreaterThan(amount, foreignStore.maxCurrentDeposit)) {
alertStore.pushError(
`The amount is above current daily limit.\nThe max withdrawal today: ${
foreignStore.maxCurrentDeposit
} ${foreignStore.symbol}`
)
return
}
if(isGreaterThan(amount, foreignStore.balance)){
alertStore.pushError(`Insufficient token balance. Your balance is ${foreignStore.balance} ${foreignStore.symbol}`)
if (isGreaterThan(amount, foreignStore.balance)) {
alertStore.pushError(
`Insufficient token balance. Your balance is ${foreignStore.balance} ${foreignStore.symbol}`
)
} else {
try {
alertStore.setLoading(true)
@ -139,18 +165,18 @@ export class Bridge extends React.Component {
return await txStore.erc20transfer({
to: foreignStore.FOREIGN_BRIDGE_ADDRESS,
from: web3Store.defaultAccount.address,
value: toDecimals(amount,foreignStore.tokenDecimals)
value: toDecimals(amount, foreignStore.tokenDecimals)
})
} else {
return await txStore.erc677transferAndCall({
to: foreignStore.FOREIGN_BRIDGE_ADDRESS,
from: web3Store.defaultAccount.address,
value: toHex(toDecimals(amount,foreignStore.tokenDecimals)),
value: toHex(toDecimals(amount, foreignStore.tokenDecimals)),
contract: foreignStore.tokenContract,
tokenAddress: foreignStore.tokenAddress
})
}
} catch(e) {
} catch (e) {
console.error(e)
}
}
@ -160,19 +186,21 @@ export class Bridge extends React.Component {
isGreaterThan = (amount, base) => new BN(amount).gt(new BN(base))
onTransfer = async (e) => {
onTransfer = async e => {
e.preventDefault()
const amount = this.state.amount.trim();
if(!amount){
swal("Error", "Please specify amount", "error")
const amount = this.state.amount.trim()
if (!amount) {
swal('Error', 'Please specify amount', 'error')
return
}
const { foreignStore, web3Store, homeStore } = this.props.RootStore
if((web3Store.metamaskNotSetted && web3Store.metamaskNet.name === '')
|| web3Store.defaultAccount.address === undefined) {
if (
(web3Store.metamaskNotSetted && web3Store.metamaskNet.name === '') ||
web3Store.defaultAccount.address === undefined
) {
web3Store.showInstallMetamaskAlert()
return
}
@ -185,7 +213,7 @@ export class Bridge extends React.Component {
let finalAmount = new BN(amount)
const feeToApply = getFeeToApply(homeStore.feeManager, foreignStore.feeManager, !reverse)
if(validFee(feeToApply)) {
if (validFee(feeToApply)) {
fee = feeToApply.multipliedBy(100)
finalAmount = finalAmount.multipliedBy(1 - feeToApply)
}
@ -201,28 +229,31 @@ export class Bridge extends React.Component {
reverse
}
this.setState({ showConfirmation: true, confirmationData})
this.setState({ showConfirmation: true, confirmationData })
}
onTransferConfirmation = async () => {
const { alertStore } = this.props.RootStore
const { reverse } = this.state
this.setState({showConfirmation: false, confirmationData: {}})
const amount = this.state.amount.trim();
if(!amount){
swal("Error", "Please specify amount", "error")
this.setState({ showConfirmation: false, confirmationData: {} })
const amount = this.state.amount.trim()
if (!amount) {
swal('Error', 'Please specify amount', 'error')
return
}
try {
if(reverse){
if (reverse) {
await this._sendToForeign(amount)
} else {
await this._sendToHome(amount)
}
} catch(e) {
if(!e.message.includes('not mined within 50 blocks') && !e.message.includes('Failed to subscribe to new newBlockHeaders')) {
} catch (e) {
if (
!e.message.includes('not mined within 50 blocks') &&
!e.message.includes('Failed to subscribe to new newBlockHeaders')
) {
alertStore.setLoading(false)
}
}
@ -231,7 +262,8 @@ export class Bridge extends React.Component {
loadHomeDetails = () => {
const { web3Store, homeStore, bridgeMode } = this.props.RootStore
const isErcToErcMode = bridgeMode === BRIDGE_MODES.ERC_TO_ERC
const isExternalErc20 = bridgeMode === BRIDGE_MODES.ERC_TO_ERC || bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE
const isExternalErc20 =
bridgeMode === BRIDGE_MODES.ERC_TO_ERC || bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE
const modalData = {
isHome: true,
@ -284,28 +316,24 @@ export class Bridge extends React.Component {
this.setState({ modalData, showModal: true })
}
getNetworkTitle = (networkName) => {
const index = networkName.indexOf(" ")
getNetworkTitle = networkName => {
const index = networkName.indexOf(' ')
if (index === -1) {
return networkName
}
return networkName.substring(0, index)
}
getNetworkSubTitle = (networkName) => {
const index = networkName.indexOf(" ")
getNetworkSubTitle = networkName => {
const index = networkName.indexOf(' ')
if (index === -1) {
return false
}
return networkName.substring(index + 1, networkName.length)
}
render() {
@ -313,11 +341,11 @@ export class Bridge extends React.Component {
const { reverse, showModal, modalData, showConfirmation, confirmationData } = this.state
const formCurrency = reverse ? foreignStore.symbol : homeStore.symbol
if(showModal && Object.keys(modalData).length !== 0) {
if(modalData.isHome && modalData.balance !== homeStore.getDisplayedBalance()) {
if (showModal && Object.keys(modalData).length !== 0) {
if (modalData.isHome && modalData.balance !== homeStore.getDisplayedBalance()) {
modalData.balance = homeStore.getDisplayedBalance()
} else if(!modalData.isHome && modalData.balance !== foreignStore.balance) {
modalData.balance= foreignStore.balance
} else if (!modalData.isHome && modalData.balance !== foreignStore.balance) {
modalData.balance = foreignStore.balance
}
}
@ -326,13 +354,10 @@ export class Bridge extends React.Component {
const foreignNetworkName = this.getNetworkTitle(foreignStore.networkName)
const foreignNetworkSubtitle = this.getNetworkSubTitle(foreignStore.networkName)
return(
return (
<div className="bridge-container">
<div className="bridge">
<BridgeAddress
isHome={true}
reverse={reverse}
/>
<BridgeAddress isHome={true} reverse={reverse} />
<div className="bridge-transfer">
<div className="left-image-wrapper">
<div className="left-image" />
@ -370,23 +395,23 @@ export class Bridge extends React.Component {
<div className="right-image" />
</div>
</div>
<BridgeAddress
isHome={false}
reverse={reverse}
/>
<BridgeAddress isHome={false} reverse={reverse} />
<ModalContainer
hideModal={() => {this.setState({showModal: false})}}
hideModal={() => {
this.setState({ showModal: false })
}}
showModal={showModal}
>
<NetworkDetails {...modalData}/>
<NetworkDetails {...modalData} />
</ModalContainer>
<ModalContainer
showModal={showConfirmation}
>
<ModalContainer showModal={showConfirmation}>
<TransferAlert
onConfirmation={this.onTransferConfirmation}
onCancel={() => {this.setState({showConfirmation: false, confirmationData: {}})}}
{...confirmationData} />
onCancel={() => {
this.setState({ showConfirmation: false, confirmationData: {} })
}}
{...confirmationData}
/>
</ModalContainer>
</div>
</div>

View File

@ -1,27 +1,30 @@
import React from 'react'
export const BridgeAddress = ({ isHome, reverse }) => {
const getAddress = () => isHome ?
(<div className="home-address-container" />)
:
(<div className="foreign-address-container" />)
const getAddress = () =>
isHome ? (
<div className="home-address-container" />
) : (
<div className="foreign-address-container" />
)
return isHome ?
(<div className="bridge-home">
return isHome ? (
<div className="bridge-home">
<div className="bridge-home-container">
<div className="home-logo-container">
<div className={reverse ? 'foreign-logo' : 'home-logo'} />
</div>
</div>
{getAddress()}
</div>)
:
(<div className="bridge-foreign">
</div>
) : (
<div className="bridge-foreign">
{getAddress()}
<div className="bridge-foreign-container">
<div className="foreign-logo-container">
<div className={reverse ? 'home-logo' : 'foreign-logo'} />
</div>
</div>
</div>)
</div>
)
}

View File

@ -1,22 +1,27 @@
import React from 'react'
export const BridgeForm = ({ reverse, currency, onTransfer, onInputChange, displayArrow}) => (
<div className={`form-container ${displayArrow ? 'transfer-right' : ''}` }>
export const BridgeForm = ({ reverse, currency, onTransfer, onInputChange, displayArrow }) => (
<div className={`form-container ${displayArrow ? 'transfer-right' : ''}`}>
<form className="bridge-form" onSubmit={onTransfer} autoComplete="off">
<div className="bridge-form-controls">
<div className="bridge-form-input-container">
<input
onChange={onInputChange}
name='amount'
pattern="[0-9]+([.][0-9]{1,18})?"
type="text"
className="bridge-form-input"
id="amount"
placeholder="0" />
<label htmlFor="amount" className="bridge-form-label">{currency}</label>
onChange={onInputChange}
name="amount"
pattern="[0-9]+([.][0-9]{1,18})?"
type="text"
className="bridge-form-input"
id="amount"
placeholder="0"
/>
<label htmlFor="amount" className="bridge-form-label">
{currency}
</label>
</div>
<div>
<button type="submit" className="bridge-form-button">Transfer</button>
<button type="submit" className="bridge-form-button">
Transfer
</button>
</div>
</div>
</form>

View File

@ -16,28 +16,37 @@ export const BridgeNetwork = ({
? numeral(0).format('0,0.00', Math.floor)
: numeral(balance).format('0,0.00', Math.floor)
const showMore = () => isHome ?
(<div className="bridge-network-data" onClick={showModal}>
<span className="info-icon info-icon-left"><InfoIcon /></span>
<span className="network-show-more">Show More</span>
</div>)
:
(<div className="bridge-network-data" onClick={showModal}>
<span className="network-show-more">Show More</span>
<span className="info-icon info-icon-right"><InfoIcon /></span>
</div>)
const showMore = () =>
isHome ? (
<div className="bridge-network-data" onClick={showModal}>
<span className="info-icon info-icon-left">
<InfoIcon />
</span>
<span className="network-show-more">Show More</span>
</div>
) : (
<div className="bridge-network-data" onClick={showModal}>
<span className="network-show-more">Show More</span>
<span className="info-icon info-icon-right">
<InfoIcon />
</span>
</div>
)
return (
<div className={`network-container-${containerName}`}>
<p className={`${ side ? `text-${side}` : ''}`}>
<p className={`${side ? `text-${side}` : ''}`}>
<span className="network-title">{networkTitle}</span>
{networkSubtitle ? <span className="network-name">{networkSubtitle}</span> : null}
</p>
<p>
<span className="network-basic-label">Balance:</span>
<span className="network-balance"> {formattedBalance} {currency}</span>
<span className="network-balance">
{' '}
{formattedBalance} {currency}
</span>
</p>
{showMore()}
</div>
)
)
}

View File

@ -2,13 +2,17 @@ import React from 'react'
import numeral from 'numeral'
import { DataBlock } from './DataBlock'
export const BridgeStatistics = ({ users, totalBridged, homeBalance, homeNativeSupplyTitle, foreignSupply, homeSymbol, foreignSymbol }) => (
export const BridgeStatistics = ({
users,
totalBridged,
homeBalance,
homeNativeSupplyTitle,
foreignSupply,
homeSymbol,
foreignSymbol
}) => (
<div className="statistics-bridge-data">
<DataBlock
description="Users"
value={numeral(users).format('0,0')}
type=''
/>
<DataBlock description="Users" value={numeral(users).format('0,0')} type="" />
<div className="separator" />
<DataBlock
description={`Total ${foreignSymbol} Bridged`}
@ -28,4 +32,4 @@ export const BridgeStatistics = ({ users, totalBridged, homeBalance, homeNativeS
type={foreignSymbol}
/>
</div>
);
)

View File

@ -2,34 +2,34 @@ import React from 'react'
import numeral from 'numeral'
import { DataBlock } from './DataBlock'
export const Configuration = ({ requiredSignatures, authorities, symbol, maxSingleDeposit, maxTotalBalance }) => (
export const Configuration = ({
requiredSignatures,
authorities,
symbol,
maxSingleDeposit,
maxTotalBalance
}) => (
<div className="status-configuration-data">
<DataBlock
description="Required Signatures"
value={numeral(requiredSignatures).format('0')}
type=''
type=""
/>
<div className="separator" />
<DataBlock
description="Authorities"
value={numeral(authorities).format('0')}
type=''
/>
{maxSingleDeposit && maxSingleDeposit !== '0'
&& <div className="separator" />
&& <DataBlock
<DataBlock description="Authorities" value={numeral(authorities).format('0')} type="" />
{maxSingleDeposit && maxSingleDeposit !== '0' && <div className="separator" /> && (
<DataBlock
description="Max Single Deposit"
value={numeral(maxSingleDeposit).format('0.00 a', Math.floor)}
type={symbol}
/>
}
{maxSingleDeposit && maxSingleDeposit !== '0'
&& <div className="separator" />
&& <DataBlock
)}
{maxSingleDeposit && maxSingleDeposit !== '0' && <div className="separator" /> && (
<DataBlock
description={`Remaining Daily ${symbol} Quota`}
value={numeral(maxTotalBalance).format('0.00 a', Math.floor)}
type={symbol}
/>
}
)}
</div>
);
)

View File

@ -1,9 +1,8 @@
import React from 'react'
import { inject, observer } from "mobx-react"
import { inject, observer } from 'mobx-react'
import numeral from 'numeral'
@inject("RootStore")
@inject('RootStore')
@observer
export class DailyQuotaModal extends React.Component {
state = {
@ -17,11 +16,11 @@ export class DailyQuotaModal extends React.Component {
getPosition = () => {
const offsetsElement = document.getElementsByClassName('header-wallet')
if(offsetsElement.length > 0) {
const offsets = offsetsElement[0].getBoundingClientRect();
const height = offsets.height;
const left = offsets.left;
this.setState({left, top: height + 20})
if (offsetsElement.length > 0) {
const offsets = offsetsElement[0].getBoundingClientRect()
const height = offsets.height
const left = offsets.left
this.setState({ left, top: height + 20 })
} else {
setTimeout(this.getPosition, 100)
}
@ -38,18 +37,18 @@ export class DailyQuotaModal extends React.Component {
const to = isHome ? foreignStore.symbol : homeStore.symbol
const networkNameFrom = isHome ? homeStore.networkName : foreignStore.networkName
const networkNameTo = isHome ? foreignStore.networkName : homeStore.networkName
const description = limit && limit !== '0' ? `${numeral(value).format('0,0.0', Math.floor)} ${from} on ${networkNameFrom + ' '}
const description =
limit && limit !== '0'
? `${numeral(value).format('0,0.0', Math.floor)} ${from} on ${networkNameFrom + ' '}
remaining for transfer to ${to + ' '}
on ${networkNameTo}`
: `No limit configured`
: `No limit configured`
return (
<div className="daily-quota-modal-container">
<div className="daily-quota-modal" style={{left, top}}>
<div className='modal-container'>
<div className="daily-quota-modal" style={{ left, top }}>
<div className="modal-container">
<span className="daily-quota-title">Daily Quota</span>
<span className="daily-quota-description">
{description}
</span>
<span className="daily-quota-description">{description}</span>
</div>
</div>
</div>

View File

@ -4,8 +4,8 @@ export const DataBlock = ({ description, value, type, dataTestid }) => (
<div className="datablock-container" data-testid={dataTestid}>
<p>
<span className="datablock-value">{value}</span>
<span className={ type ? "datablock-type" : ""}>{type}</span>
<span className={type ? 'datablock-type' : ''}>{type}</span>
</p>
<p className="datablock-description">{description}</p>
</div>
);
)

View File

@ -4,33 +4,45 @@ import disclaimerIcon from '../assets/images/disclaimer-modal/disclaimer@2x.png'
export const Disclaimer = ({ onConfirmation }) => (
<div className="disclaimer-alert">
<div className="image-container">
<img className="disclaimer-icon" src={disclaimerIcon} alt="disclaimer icon"/>
<img className="disclaimer-icon" src={disclaimerIcon} alt="disclaimer icon" />
</div>
<div className="alert-container">
<span className="disclaimer-title">Welcome to the<br/> TokenBridge UI App Beta+</span>
<span className="disclaimer-title">
Welcome to the
<br /> TokenBridge UI App Beta+
</span>
<p className="disclaimer-description">
Were launching our TokenBridge and our UI App on a beta-testing basis. While weve
worked long and hard to develop the core features of the software, we expect that our
users may detect bugs and other issues. Help us improve by posting any difficulties to our
<a href="https://forum.poa.network/c/support/tokenbridge-support" target="_blank"
rel="noopener noreferrer"> support page</a>.
<br/>
<br/>
Use of this app and the TokenBridge is at your own risk. Users may experience
unexpected delays, unexpected visual artifacts, unexpected loss of tokens or funds from
improper app configuration, or other negative outcomes.
<br/>
<br/>
Were launching our TokenBridge and our UI App on a beta-testing basis. While weve worked
long and hard to develop the core features of the software, we expect that our users may
detect bugs and other issues. Help us improve by posting any difficulties to our
<a
href="https://forum.poa.network/c/support/tokenbridge-support"
target="_blank"
rel="noopener noreferrer"
>
{' '}
support page
</a>
.
<br />
<br />
Use of this app and the TokenBridge is at your own risk. Users may experience unexpected
delays, unexpected visual artifacts, unexpected loss of tokens or funds from improper app
configuration, or other negative outcomes.
<br />
<br />
By hitting the "continue" button, you are representing that youve read our
<a
href="https://forum.poa.network/t/end-user-licensing-agreement-and-terms-of-service/2197"
target="_blank" rel="noopener noreferrer">Terms of
Service</a> in full, and that you agree to be legally bound by them.
target="_blank"
rel="noopener noreferrer"
>
Terms of Service
</a>{' '}
in full, and that you agree to be legally bound by them.
</p>
<div className="disclaimer-buttons">
<button
className="disclaimer-confirm"
onClick={onConfirmation}>
<button className="disclaimer-confirm" onClick={onConfirmation}>
Continue
</button>
</div>

View File

@ -1,11 +1,26 @@
import React from 'react'
export const EventsListHeader = ({selected, homeName, homeValue, foreignName, foreignValue, onChangeList, handleChange, handleKeyDown}) => (
export const EventsListHeader = ({
selected,
homeName,
homeValue,
foreignName,
foreignValue,
onChangeList,
handleChange,
handleKeyDown
}) => (
<div>
<div className="events-header">
<span className="events-header-title">Events</span>
<div className="events-filter-container">
<input onChange={handleChange} onKeyDown={handleKeyDown} type="text" className="events-filter" placeholder="Tx Hash or Block Number..." />
<input
onChange={handleChange}
onKeyDown={handleKeyDown}
type="text"
className="events-filter"
placeholder="Tx Hash or Block Number..."
/>
<span className="events-filter-icon" />
<select value={selected} onChange={onChangeList} className="events-select">
<option value={homeValue}>{homeName}</option>

View File

@ -2,11 +2,7 @@ import React from 'react'
import { CSSTransition } from 'react-transition-group'
export const Fade = ({ children, ...props }) => (
<CSSTransition
{...props}
timeout={1000}
classNames="fade"
>
<CSSTransition {...props} timeout={1000} classNames="fade">
{children}
</CSSTransition>
)

View File

@ -3,35 +3,30 @@ import numeral from 'numeral'
import { DataBlock } from './DataBlock'
export const FeeStatistics = ({ depositFeeCollected, withdrawFeeCollected }) => (
<div className='statistics-fee-container' data-testid="fee-statistics">
{
(depositFeeCollected.shouldDisplay || withdrawFeeCollected.shouldDisplay)
&& <span className='statistics-bridge-title statistics-title'>Fee Statistics</span>
}
<div className="statistics-fee-container" data-testid="fee-statistics">
{(depositFeeCollected.shouldDisplay || withdrawFeeCollected.shouldDisplay) && (
<span className="statistics-bridge-title statistics-title">Fee Statistics</span>
)}
<div className="statistics-fee-data" data-testid="fee-statistics-data">
{
depositFeeCollected.shouldDisplay &&
{depositFeeCollected.shouldDisplay && (
<DataBlock
description="Deposit Fees"
value={numeral(depositFeeCollected.value).format('0,0.00 a', Math.floor)}
type={depositFeeCollected.type}
dataTestid="deposit-fees-block"
/>
}
{
depositFeeCollected.shouldDisplay &&
withdrawFeeCollected.shouldDisplay &&
)}
{depositFeeCollected.shouldDisplay && withdrawFeeCollected.shouldDisplay && (
<div className="separator" />
}
{
withdrawFeeCollected.shouldDisplay &&
)}
{withdrawFeeCollected.shouldDisplay && (
<DataBlock
description="Withdrawal Fees"
value={numeral(withdrawFeeCollected.value).format('0,0.00 a', Math.floor)}
type={withdrawFeeCollected.type}
dataTestid="withdrawal-fees-block"
/>
}
)}
</div>
</div>
)

View File

@ -1,5 +1,5 @@
import React from 'react'
import { Link } from 'react-router-dom'
import { Link } from 'react-router-dom'
import { SocialIcons } from './SocialIcons'
export const Footer = () => (

View File

@ -2,28 +2,38 @@ import React from 'react'
import yn from './utils/yn'
import { DailyQuotaModal } from './DailyQuotaModal'
import { HeaderMenu } from './HeaderMenu'
import { Link } from 'react-router-dom'
import { Link } from 'react-router-dom'
import { MobileMenu } from './MobileMenu'
import { MobileMenuButton } from './MobileMenuButton'
import { inject, observer } from 'mobx-react/index'
@inject("RootStore")
@inject('RootStore')
@observer
export class Header extends React.Component {
render () {
const { showMobileMenu, onMenuToggle, RootStore: { alertStore, web3Store } } = this.props
const { REACT_APP_HOME_WITHOUT_EVENTS: HOME, REACT_APP_FOREIGN_WITHOUT_EVENTS: FOREIGN } = process.env
const withoutEvents = web3Store.metamaskNet.id === web3Store.homeNet.id.toString() ? yn(HOME) : yn(FOREIGN)
export class Header extends React.Component {
render() {
const {
showMobileMenu,
onMenuToggle,
RootStore: { alertStore, web3Store }
} = this.props
const {
REACT_APP_HOME_WITHOUT_EVENTS: HOME,
REACT_APP_FOREIGN_WITHOUT_EVENTS: FOREIGN
} = process.env
const withoutEvents =
web3Store.metamaskNet.id === web3Store.homeNet.id.toString() ? yn(HOME) : yn(FOREIGN)
return (
<header className="header">
{showMobileMenu ? <MobileMenu withoutEvents={withoutEvents} onMenuToggle={onMenuToggle} /> : null}
{showMobileMenu ? (
<MobileMenu withoutEvents={withoutEvents} onMenuToggle={onMenuToggle} />
) : null}
<div className="container">
<Link to="/" onClick={showMobileMenu ? onMenuToggle : null} className="header-logo" />
<HeaderMenu withoutEvents={withoutEvents} onMenuToggle={onMenuToggle} />
<MobileMenuButton onMenuToggle={onMenuToggle} showMobileMenu={showMobileMenu} />
</div>
{alertStore && alertStore.showDailyQuotaInfo && <DailyQuotaModal/>}
{alertStore && alertStore.showDailyQuotaInfo && <DailyQuotaModal />}
</header>
)
}

View File

@ -1,5 +1,5 @@
import React from "react";
import { MenuItems } from "./MenuItems";
import React from 'react'
import { MenuItems } from './MenuItems'
import { Wallet } from './Wallet'
export const HeaderMenu = ({ withoutEvents }) => (
@ -7,4 +7,4 @@ export const HeaderMenu = ({ withoutEvents }) => (
<MenuItems withoutEvents={withoutEvents} />
<Wallet />
</div>
);
)

View File

@ -1,29 +1,36 @@
import React from 'react';
import { inject, observer } from "mobx-react";
import React from 'react'
import { inject, observer } from 'mobx-react'
import { ProgressRing } from './ProgressRing'
import { PreventExit } from './PreventExit'
@inject("RootStore")
@inject('RootStore')
@observer
export class Loading extends React.Component {
render() {
const { alertStore } = this.props.RootStore
const { loadingStepIndex, loadingSteps, blockConfirmations } = alertStore
const style = alertStore.showLoading ? {display: 'flex'} : {display: 'none'}
const progress = loadingStepIndex === 3 ? 100 : (loadingStepIndex) * 25 + blockConfirmations * 4
const style = alertStore.showLoading ? { display: 'flex' } : { display: 'none' }
const progress = loadingStepIndex === 3 ? 100 : loadingStepIndex * 25 + blockConfirmations * 4
return (
<div className={`loading-container ${loadingStepIndex > 0 ? 'mobile-container' : ''}`} style={style}>
{loadingStepIndex > 0 && <ProgressRing
confirmationNumber={blockConfirmations}
hideConfirmationNumber={loadingStepIndex > 1}
progress={progress}
radius={ 40 }
stroke={ 4 }
/>}
{loadingStepIndex === 0 && (<div className="loading-logo" />)}
<div
className={`loading-container ${loadingStepIndex > 0 ? 'mobile-container' : ''}`}
style={style}
>
{loadingStepIndex > 0 && (
<ProgressRing
confirmationNumber={blockConfirmations}
hideConfirmationNumber={loadingStepIndex > 1}
progress={progress}
radius={40}
stroke={4}
/>
)}
{loadingStepIndex === 0 && <div className="loading-logo" />}
{loadingStepIndex === 0 && <div className="loading-i" />}
{loadingStepIndex > 0 && (<div className="loading-text">{loadingSteps[loadingStepIndex]}</div>)}
{loadingStepIndex > 0 && (
<div className="loading-text">{loadingSteps[loadingStepIndex]}</div>
)}
{alertStore.showLoading && <PreventExit />}
</div>
)

View File

@ -1,26 +1,26 @@
import React from "react"
import { EventsIcon, StatusIcon, StatisticsIcon } from "./menu-icons"
import { Link } from "react-router-dom"
import React from 'react'
import { EventsIcon, StatusIcon, StatisticsIcon } from './menu-icons'
import { Link } from 'react-router-dom'
export const MenuItems = ({ onMenuToggle = null, withoutEvents }) => {
const menuItems = [
{
hide: withoutEvents,
icon: <EventsIcon />,
link: "/events",
text: "Events"
link: '/events',
text: 'Events'
},
{
hide: false,
icon: <StatusIcon />,
link: "/status",
text: "Status"
link: '/status',
text: 'Status'
},
{
hide: withoutEvents,
icon: <StatisticsIcon />,
link: "/statistics",
text: "Statistics"
link: '/statistics',
text: 'Statistics'
}
]
@ -30,6 +30,6 @@ export const MenuItems = ({ onMenuToggle = null, withoutEvents }) => {
<span className="menu-items-icon">{item.icon}</span>
<span className="menu-items-text">{item.text}</span>
</Link>
);
)
})
}

View File

@ -1,5 +1,5 @@
import React from "react";
import { MenuItems } from "./MenuItems";
import React from 'react'
import { MenuItems } from './MenuItems'
export const MobileMenu = ({ onMenuToggle, withoutEvents }) => (
<div className="mobile-menu">
@ -7,4 +7,4 @@ export const MobileMenu = ({ onMenuToggle, withoutEvents }) => (
<MenuItems withoutEvents={withoutEvents} onMenuToggle={onMenuToggle} />
</div>
</div>
);
)

View File

@ -4,8 +4,8 @@ import { MobileMenuCloseIcon } from './menu-icons/MobileMenuCloseIcon'
export const MobileMenuButton = ({ showMobileMenu, onMenuToggle }) => {
return (
<div className='mobile-menu-button' onClick={onMenuToggle}>
<div className="mobile-menu-button" onClick={onMenuToggle}>
{showMobileMenu ? <MobileMenuCloseIcon /> : <MobileMenuIcon />}
</div>
)
}
}

View File

@ -1,23 +1,30 @@
import React from 'react'
export const ModalContainer = (props) => {
export const ModalContainer = props => {
if (props.showModal)
return (
<div className="network-modal loading-container"
onClick={props.hideModal
? (e) => {
if(e.target.classList.contains('network-details') || e.target.classList.contains('loading-container')) {
props.hideModal()
<div
className="network-modal loading-container"
onClick={
props.hideModal
? e => {
if (
e.target.classList.contains('network-details') ||
e.target.classList.contains('loading-container')
) {
props.hideModal()
}
}
}
: () => {}}
: () => {}
}
>
<div className='modal'>
<div className="modal">
{props.children}
{props.hideModal
? <div className='close-button' onClick={() => props.hideModal()}><i className="icon"/></div>
: null
}
{props.hideModal ? (
<div className="close-button" onClick={() => props.hideModal()}>
<i className="icon" />
</div>
) : null}
</div>
</div>
)

View File

@ -22,11 +22,13 @@ export const NetworkDetails = ({
nativeSupplyTitle,
tokenName,
getExplorerAddressUrl
}) => {
}) => {
const networkTitle = isHome ? 'Bridge Home' : 'Bridge Foreign'
const logoClass = isHome ? 'home-logo home-logo-modal' : 'foreign-logo foreign-logo-modal'
const totalTitle = isHome
? nativeSupplyTitle ? `Native Coins Amount` : `Totally minted by the bridge`
? nativeSupplyTitle
? `Native Coins Amount`
: `Totally minted by the bridge`
: `${currency} Tokens Amount`
const totalAmount = isHome ? totalBalance : totalSupply
const formattedBalance = isNaN(numeral(balance).format('0.00', Math.floor))
@ -35,8 +37,8 @@ export const NetworkDetails = ({
return (
<div className="network-details" data-testid="network-details">
<div className="details-logo-container">
<div className={logoClass} />
<div className="details-logo-container">
<div className={logoClass} />
</div>
<div className="details-body">
<p className="details-data-container">
@ -45,36 +47,60 @@ export const NetworkDetails = ({
</p>
<p className="details-data-container">
<span className="details-label">{networkTitle} Address</span>
<span className="details-description details-copy">
<a className="details-description" href={getExplorerAddressUrl(address)} target="_blank" >
{address.slice(0,27).concat('...')}
</a>
<CopyToClipboard text={address}>
<span className="copy-icon copy-icon-right"><CopyIcon /></span>
</CopyToClipboard>
</span>
<span className="details-description details-copy">
<a
className="details-description"
href={getExplorerAddressUrl(address)}
target="_blank"
>
{address.slice(0, 27).concat('...')}
</a>
<CopyToClipboard text={address}>
<span className="copy-icon copy-icon-right">
<CopyIcon />
</span>
</CopyToClipboard>
</span>
</p>
{displayBridgeLimits && <p className="details-data-container">
<span className="details-label">Remaining Daily {currency} Quota</span>
<span className="details-description-black">{numeral(maxCurrentLimit).format('0,0.0', Math.floor)} {currency}</span>
</p>}
{displayBridgeLimits && <p className="details-data-container">
<span className="details-label">Maximum Amount Per Transaction</span>
<span className="details-description-black">{numeral(maxPerTx).format('0,0.0', Math.floor)} {currency}</span>
</p>}
{displayBridgeLimits && <p className="details-data-container">
<span className="details-label">Minimum Amount Per Transaction</span>
<span className="details-description-black">{numeral(minPerTx).format('0,0.000', Math.floor)} {currency}</span>
</p>}
{displayBridgeLimits && (
<p className="details-data-container">
<span className="details-label">Remaining Daily {currency} Quota</span>
<span className="details-description-black">
{numeral(maxCurrentLimit).format('0,0.0', Math.floor)} {currency}
</span>
</p>
)}
{displayBridgeLimits && (
<p className="details-data-container">
<span className="details-label">Maximum Amount Per Transaction</span>
<span className="details-description-black">
{numeral(maxPerTx).format('0,0.0', Math.floor)} {currency}
</span>
</p>
)}
{displayBridgeLimits && (
<p className="details-data-container">
<span className="details-label">Minimum Amount Per Transaction</span>
<span className="details-description-black">
{numeral(minPerTx).format('0,0.000', Math.floor)} {currency}
</span>
</p>
)}
{displayTokenAddress && (
<p className="details-data-container">
<span className="details-label">Token Address</span>
<span className="details-description details-copy">
<a className="details-description" href={getExplorerAddressUrl(tokenAddress)} target="_blank" >
{tokenAddress.slice(0,27).concat('...')}
<a
className="details-description"
href={getExplorerAddressUrl(tokenAddress)}
target="_blank"
>
{tokenAddress.slice(0, 27).concat('...')}
</a>
<CopyToClipboard text={tokenAddress}>
<span className="copy-icon copy-icon-right"><CopyIcon /></span>
<span className="copy-icon copy-icon-right">
<CopyIcon />
</span>
</CopyToClipboard>
</span>
</p>
@ -87,11 +113,15 @@ export const NetworkDetails = ({
)}
<p className="details-data-container">
<span className="details-label">{totalTitle}</span>
<span className="details-description-black">{numeral(totalAmount).format('0,0.000', Math.floor)} {currency}</span>
<span className="details-description-black">
{numeral(totalAmount).format('0,0.000', Math.floor)} {currency}
</span>
</p>
<p className="details-data-container">
<span className="details-label">Your {currency} Balance</span>
<span className="details-description-black">{formattedBalance} {currency}</span>
<span className="details-description-black">
{formattedBalance} {currency}
</span>
</p>
</div>
</div>

View File

@ -15,7 +15,12 @@ export class NoWallet extends Component {
}
render() {
const { RootStore: { web3Store: { walletInstalled } }, showModal: showNoWallet } = this.props
const {
RootStore: {
web3Store: { walletInstalled }
},
showModal: showNoWallet
} = this.props
const showModal = showNoWallet && !walletInstalled
if (!showModal || !this.state.showModal) return null
@ -24,14 +29,18 @@ export class NoWallet extends Component {
<ModalContainer showModal={showModal && this.state.showModal}>
<div className="noWallet-alert">
<div className="noWallet-image-container">
<img className="noWallet-icon" src={noWalletIcon} alt="no wallet icon"/>
<img className="noWallet-icon" src={noWalletIcon} alt="no wallet icon" />
</div>
<div className="noWallet-alert-container">
<h2 className="noWallet-title">Wallet not found</h2>
<p className="noWallet-description">A wallet is not installed. Before continue, please install one (AlphaWallet, Metamask
or Nifty Wallet) and return to this page to continue using the application.</p>
<p className="noWallet-description">For further information on how to install any of both wallets, please
click the buttons below.</p>
<p className="noWallet-description">
A wallet is not installed. Before continue, please install one (AlphaWallet, Metamask
or Nifty Wallet) and return to this page to continue using the application.
</p>
<p className="noWallet-description">
For further information on how to install any of both wallets, please click the
buttons below.
</p>
<div className="noWallet-buttons">
<a
className="noWallet-metamask"
@ -50,14 +59,16 @@ export class NoWallet extends Component {
Nifty Wallet
</a>
<a
className="noWallet-alphawallet"
href="https://alphawallet.github.io/AlphaWallet-Download-Page/"
rel="noopener noreferrer"
target="_blank"
className="noWallet-alphawallet"
href="https://alphawallet.github.io/AlphaWallet-Download-Page/"
rel="noopener noreferrer"
target="_blank"
>
AlphaWallet
AlphaWallet
</a>
<button className="noWallet-cancel" onClick={this.handleCancel}>Cancel</button>
<button className="noWallet-cancel" onClick={this.handleCancel}>
Cancel
</button>
</div>
</div>
</div>

View File

@ -1,19 +1,19 @@
import React, {Component} from 'react'
import React, { Component } from 'react'
export class PreventExit extends Component {
onUnload = (e) => {
e.returnValue = "Are you sure?"
onUnload = e => {
e.returnValue = 'Are you sure?'
}
componentDidMount () {
componentDidMount() {
window.addEventListener('beforeunload', this.onUnload)
}
componentWillUnmount () {
componentWillUnmount() {
window.removeEventListener('beforeunload', this.onUnload)
}
render () {
render() {
return <div />
}
}

View File

@ -1,4 +1,4 @@
import React, { Component } from 'react';
import React, { Component } from 'react'
export class ProgressRing extends Component {
state = {
@ -9,37 +9,34 @@ export class ProgressRing extends Component {
render() {
const { radius, stroke, progress, confirmationNumber, hideConfirmationNumber } = this.props
const { circumference, normalizedRadius } = this.state
const strokeDashoffset = circumference - progress / 100 * circumference
const strokeDashoffset = circumference - (progress / 100) * circumference
const confirmations = hideConfirmationNumber ? '' : `${confirmationNumber}/8`
return (
<svg
height={radius * 2}
width={radius * 2}
>
<svg height={radius * 2} width={radius * 2}>
<circle
stroke="#7b5ab2"
fill="transparent"
strokeWidth={ stroke }
strokeDasharray={ circumference + ' ' + circumference }
style={ { strokeDashoffset: 0 } }
r={ normalizedRadius }
cx={ radius }
cy={ radius }
strokeWidth={stroke}
strokeDasharray={circumference + ' ' + circumference}
style={{ strokeDashoffset: 0 }}
r={normalizedRadius}
cx={radius}
cy={radius}
/>
<circle
stroke="#60dc97"
fill="transparent"
strokeWidth={ stroke }
strokeDasharray={ circumference + ' ' + circumference }
style={ { strokeDashoffset } }
r={ normalizedRadius }
cx={ radius }
cy={ radius }
strokeWidth={stroke}
strokeDasharray={circumference + ' ' + circumference}
style={{ strokeDashoffset }}
r={normalizedRadius}
cx={radius}
cy={radius}
/>
<text x="28" y="47" fontFamily="Nunito" fontSize="18" fill="white">
{confirmations}
</text>
</svg>
);
)
}
}

View File

@ -1,28 +1,27 @@
import React from 'react';
import { inject, observer } from "mobx-react";
import React from 'react'
import { inject, observer } from 'mobx-react'
import { EventsListHeader } from './index'
import { Event } from './index'
import yn from './utils/yn'
import { Redirect } from 'react-router'
const WAIT_INTERVAL = 700
const ENTER_KEY = 13
const WAIT_INTERVAL = 700;
const ENTER_KEY = 13;
@inject("RootStore")
@inject('RootStore')
@observer
export class RelayEvents extends React.Component {
constructor(props){
constructor(props) {
super(props)
this.timer = null;
this.timer = null
this.colors = {
'UserRequestForSignature': 'green',
'RelayedMessage': 'green',
'UserRequestForAffirmation': 'red',
'AffirmationCompleted': 'red',
'SignedForUserRequest': 'purple',
'SignedForAffirmation': 'purple',
'CollectedSignatures': 'blue'
UserRequestForSignature: 'green',
RelayedMessage: 'green',
UserRequestForAffirmation: 'red',
AffirmationCompleted: 'red',
SignedForUserRequest: 'purple',
SignedForAffirmation: 'purple',
CollectedSignatures: 'blue'
}
this.homeValue = '0'
this.foreingValue = '1'
@ -31,15 +30,15 @@ export class RelayEvents extends React.Component {
}
}
onHomeBlockFilter = async (value) => {
onHomeBlockFilter = async value => {
const { alertStore, homeStore, foreignStore } = this.props.RootStore
alertStore.setLoading(true)
if(value.substr(0,2) === "0x"){
if (value.substr(0, 2) === '0x') {
homeStore.setFilter(true)
foreignStore.setFilter(true)
await homeStore.filterByTxHash(value)
} else {
if(Number(value) > 0){
if (Number(value) > 0) {
homeStore.setFilter(true)
foreignStore.setFilter(true)
await homeStore.setBlockFilter(value)
@ -53,15 +52,15 @@ export class RelayEvents extends React.Component {
alertStore.setLoading(false)
}
onForeignBlockFilter = async (value) => {
onForeignBlockFilter = async value => {
const { alertStore, homeStore, foreignStore } = this.props.RootStore
alertStore.setLoading(true)
if(value.substr(0,2) === "0x"){
if (value.substr(0, 2) === '0x') {
homeStore.setFilter(true)
foreignStore.setFilter(true)
await foreignStore.filterByTxHash(value)
} else {
if(Number(value) > 0){
if (Number(value) > 0) {
homeStore.setFilter(true)
foreignStore.setFilter(true)
await foreignStore.setBlockFilter(value)
@ -75,37 +74,42 @@ export class RelayEvents extends React.Component {
alertStore.setLoading(false)
}
handleChangeHome = async (e) => {
const value = e.target.value;
window.clearTimeout(this.timer);
this.timer = setTimeout(() => {this.onHomeBlockFilter(value)}, WAIT_INTERVAL);
handleChangeHome = async e => {
const value = e.target.value
window.clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.onHomeBlockFilter(value)
}, WAIT_INTERVAL)
}
handleChangeForeign = async (e) => {
const value = e.target.value;
window.clearTimeout(this.timer);
this.timer = setTimeout(() => {this.onForeignBlockFilter(value)}, WAIT_INTERVAL);
handleChangeForeign = async e => {
const value = e.target.value
window.clearTimeout(this.timer)
this.timer = setTimeout(() => {
this.onForeignBlockFilter(value)
}, WAIT_INTERVAL)
}
handleKeyDownHome = (e) => {
const value = e.target.value;
window.clearTimeout(this.timer);
handleKeyDownHome = e => {
const value = e.target.value
window.clearTimeout(this.timer)
if (e.keyCode === ENTER_KEY && value) {
this.onHomeBlockFilter(value);
this.onHomeBlockFilter(value)
}
}
handleKeyDownForeign = (e) => {
const value = e.target.value;
window.clearTimeout(this.timer);
handleKeyDownForeign = e => {
const value = e.target.value
window.clearTimeout(this.timer)
if (e.keyCode === ENTER_KEY && value) {
this.onForeignBlockFilter(value);
this.onForeignBlockFilter(value)
}
}
getHomeEvents = (homeStore) => {
return homeStore.events.slice().map(({event, transactionHash, blockNumber, returnValues}) =>
({
getHomeEvents = homeStore => {
return homeStore.events
.slice()
.map(({ event, transactionHash, blockNumber, returnValues }) => ({
color: this.colors[event],
eventName: event,
transactionHash,
@ -115,60 +119,76 @@ export class RelayEvents extends React.Component {
}))
}
getForeignEvents = (foreignStore) => {
return foreignStore.events.slice()
.map(({ event, transactionHash, signedTxHash, blockNumber, returnValues}) => {
return ({
getForeignEvents = foreignStore => {
return foreignStore.events
.slice()
.map(({ event, transactionHash, signedTxHash, blockNumber, returnValues }) => {
return {
color: this.colors[event],
eventName: event,
transactionHash,
recipient: returnValues.recipient,
value: returnValues.value,
blockNumber
})
}
})
}
onChangeList = (e) => {
this.setState({selectedList: e.target.value})
onChangeList = e => {
this.setState({ selectedList: e.target.value })
}
render(){
render() {
const { homeStore, foreignStore, web3Store } = this.props.RootStore
const { selectedList } = this.state
const home = this.getHomeEvents(homeStore, foreignStore)
const foreign = this.getForeignEvents(foreignStore, homeStore)
const { REACT_APP_HOME_WITHOUT_EVENTS: HOME, REACT_APP_FOREIGN_WITHOUT_EVENTS: FOREIGN } = process.env
const withoutEvents = web3Store.metamaskNet.id === web3Store.homeNet.id.toString() ? yn(HOME) : yn(FOREIGN)
const {
REACT_APP_HOME_WITHOUT_EVENTS: HOME,
REACT_APP_FOREIGN_WITHOUT_EVENTS: FOREIGN
} = process.env
const withoutEvents =
web3Store.metamaskNet.id === web3Store.homeNet.id.toString() ? yn(HOME) : yn(FOREIGN)
return withoutEvents ? (<Redirect to="/" />) : (
return withoutEvents ? (
<Redirect to="/" />
) : (
<div className="events-page">
<div className="events-container">
<EventsListHeader
handleChange={selectedList === this.homeValue ? this.handleChangeHome : this.handleChangeForeign}
handleKeyDown={selectedList === this.homeValue ? this.handleKeyDownHome : this.handleKeyDownForeign}
handleChange={
selectedList === this.homeValue ? this.handleChangeHome : this.handleChangeForeign
}
handleKeyDown={
selectedList === this.homeValue ? this.handleKeyDownHome : this.handleKeyDownForeign
}
onChangeList={this.onChangeList}
selected={selectedList}
homeName={homeStore.networkName}
homeValue={this.homeValue}
foreignName={foreignStore.networkName}
foreignValue={this.foreingValue} />
{selectedList === this.homeValue
&& home.map(event =>
<Event
txUrl={homeStore.getExplorerTxUrl(event.transactionHash)}
accountUrl={homeStore.getExplorerAddressUrl(event.recipient)}
key={event.transactionHash+event.eventName}
{...event} />)}
{selectedList === this.foreingValue
&& foreign.map(event =>
<Event
txUrl={foreignStore.getExplorerTxUrl(event.transactionHash)}
accountUrl={foreignStore.getExplorerAddressUrl(event.recipient)}
key={event.transactionHash+event.eventName}
{...event} />)}
foreignValue={this.foreingValue}
/>
{selectedList === this.homeValue &&
home.map(event => (
<Event
txUrl={homeStore.getExplorerTxUrl(event.transactionHash)}
accountUrl={homeStore.getExplorerAddressUrl(event.recipient)}
key={event.transactionHash + event.eventName}
{...event}
/>
))}
{selectedList === this.foreingValue &&
foreign.map(event => (
<Event
txUrl={foreignStore.getExplorerTxUrl(event.transactionHash)}
accountUrl={foreignStore.getExplorerAddressUrl(event.recipient)}
key={event.transactionHash + event.eventName}
{...event}
/>
))}
</div>
</div>
);
)
}
}

View File

@ -1,24 +1,24 @@
import React from "react"
import { IconGithub, IconPOA, IconTelegram, IconTwitter } from "./social-icons"
import React from 'react'
import { IconGithub, IconPOA, IconTelegram, IconTwitter } from './social-icons'
export const SocialIcons = () => {
const socialItems = [
{
icon: <IconPOA />,
link: "https://poa.network",
link: 'https://poa.network'
},
{
icon: <IconTwitter />,
link: "https://twitter.com/poanetwork",
link: 'https://twitter.com/poanetwork'
},
{
icon: <IconTelegram />,
link: "https://t.me/poa_network",
link: 'https://t.me/poa_network'
},
{
icon: <IconGithub />,
link: "https://github.com/poanetwork/token-bridge",
},
link: 'https://github.com/poanetwork/token-bridge'
}
]
return (

View File

@ -4,63 +4,75 @@ import { BRIDGE_MODES } from '../stores/utils/bridgeMode'
import { BridgeStatistics } from './index'
import { Redirect } from 'react-router'
import { TransactionsStatistics } from './TransactionsStatistics'
import { inject, observer } from "mobx-react"
import { FeeStatistics } from "./FeeStatistics"
import { inject, observer } from 'mobx-react'
import { FeeStatistics } from './FeeStatistics'
@inject("RootStore")
@inject('RootStore')
@observer
export class StatisticsPage extends React.Component {
render(){
render() {
const { homeStore, foreignStore, bridgeMode, web3Store } = this.props.RootStore
const isNativeToErc = bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC
const leftTitle = isNativeToErc ? 'Deposits' : 'Withdraws'
const rightTitle = isNativeToErc ? 'Withdraws' : 'Deposits'
const { REACT_APP_HOME_WITHOUT_EVENTS: HOME, REACT_APP_FOREIGN_WITHOUT_EVENTS: FOREIGN } = process.env
const withoutEvents = web3Store.metamaskNet.id === web3Store.homeNet.id.toString() ? yn(HOME) : yn(FOREIGN)
const {
REACT_APP_HOME_WITHOUT_EVENTS: HOME,
REACT_APP_FOREIGN_WITHOUT_EVENTS: FOREIGN
} = process.env
const withoutEvents =
web3Store.metamaskNet.id === web3Store.homeNet.id.toString() ? yn(HOME) : yn(FOREIGN)
return withoutEvents ? ( <Redirect to="/" />) : (
return withoutEvents ? (
<Redirect to="/" />
) : (
<div className="statistics-page">
<div className='statistics-left-container' />
<div className='statistics-page-container'>
<div className='statistics-bridge-container'>
<span className='statistics-bridge-title statistics-title'>Bridge Statistics</span>
<div className="statistics-left-container" />
<div className="statistics-page-container">
<div className="statistics-bridge-container">
<span className="statistics-bridge-title statistics-title">Bridge Statistics</span>
<BridgeStatistics
users={homeStore.statistics.finished ? homeStore.statistics.users.size : ''}
totalBridged={homeStore.statistics.finished ? homeStore.statistics.totalBridged.toString() : ''}
totalBridged={
homeStore.statistics.finished ? homeStore.statistics.totalBridged.toString() : ''
}
homeBalance={homeStore.balance}
homeSymbol={homeStore.symbol}
homeNativeSupplyTitle={isNativeToErc}
foreignSymbol={foreignStore.symbol}
foreignSupply={foreignStore.totalSupply} />
foreignSupply={foreignStore.totalSupply}
/>
</div>
{
homeStore.depositFeeCollected.finished
&& homeStore.withdrawFeeCollected.finished
&& (homeStore.depositFeeCollected.shouldDisplay || homeStore.withdrawFeeCollected.shouldDisplay)
&& <FeeStatistics
{homeStore.depositFeeCollected.finished &&
homeStore.withdrawFeeCollected.finished &&
(homeStore.depositFeeCollected.shouldDisplay ||
homeStore.withdrawFeeCollected.shouldDisplay) && (
<FeeStatistics
depositFeeCollected={homeStore.depositFeeCollected}
withdrawFeeCollected={homeStore.withdrawFeeCollected}
/>
}
<div className='statistics-transaction-container'>
<div className='statistics-deposit-container'>
<span className='statistics-deposit-title statistics-title'>Tokens {leftTitle}</span>
)}
<div className="statistics-transaction-container">
<div className="statistics-deposit-container">
<span className="statistics-deposit-title statistics-title">Tokens {leftTitle}</span>
<TransactionsStatistics
txNumber={homeStore.statistics.finished ? homeStore.statistics.deposits : ''}
type={foreignStore.symbol}
value={homeStore.statistics.finished ? homeStore.statistics.depositsValue : ''} />
value={homeStore.statistics.finished ? homeStore.statistics.depositsValue : ''}
/>
</div>
<div className='statistics-withdraw-container'>
<span className='statistics-withdraw-title statistics-title'>Tokens {rightTitle}</span>
<div className="statistics-withdraw-container">
<span className="statistics-withdraw-title statistics-title">
Tokens {rightTitle}
</span>
<TransactionsStatistics
txNumber={homeStore.statistics.finished ? homeStore.statistics.withdraws : ''}
type={foreignStore.symbol}
value={homeStore.statistics.finished ? homeStore.statistics.withdrawsValue : ''} />
value={homeStore.statistics.finished ? homeStore.statistics.withdrawsValue : ''}
/>
</div>
</div>
</div>
<div className='pattern-background'>
<div className="pattern-background">
<div className="pattern-background-image" />
</div>
</div>

View File

@ -2,50 +2,55 @@ import React from 'react'
import yn from './utils/yn'
import { Authority } from './Authority'
import { Configuration } from './Configuration'
import { inject, observer } from "mobx-react"
import { inject, observer } from 'mobx-react'
@inject("RootStore")
@inject('RootStore')
@observer
export class StatusPage extends React.Component {
render() {
const { homeStore, foreignStore, web3Store } = this.props.RootStore
const isHome = web3Store.metamaskNet.id.toString() === web3Store.homeNet.id.toString()
const requiredSignatures = isHome ? homeStore.requiredSignatures : foreignStore.requiredSignatures
const requiredSignatures = isHome
? homeStore.requiredSignatures
: foreignStore.requiredSignatures
const authorities = isHome ? homeStore.validatorsCount : foreignStore.validatorsCount
const symbol = isHome ? homeStore.symbol : foreignStore.symbol
const maxSingleDeposit = isHome ? homeStore.maxPerTx : foreignStore.maxPerTx
const maxTotalBalance = isHome ? homeStore.maxCurrentDeposit : foreignStore.maxCurrentDeposit
const validatorsList = isHome ? homeStore.validators : foreignStore.validators
const { REACT_APP_HOME_WITHOUT_EVENTS: HOME, REACT_APP_FOREIGN_WITHOUT_EVENTS: FOREIGN } = process.env
const withoutEvents = web3Store.metamaskNet.id === web3Store.homeNet.id.toString() ? yn(HOME) : yn(FOREIGN)
const {
REACT_APP_HOME_WITHOUT_EVENTS: HOME,
REACT_APP_FOREIGN_WITHOUT_EVENTS: FOREIGN
} = process.env
const withoutEvents =
web3Store.metamaskNet.id === web3Store.homeNet.id.toString() ? yn(HOME) : yn(FOREIGN)
return (
<div className="status-page">
<div className='status-left-container' />
<div className='status-page-container'>
<div className='status-configuration-container'>
<span className='status-configuration-title status-title'>Configuration</span>
<Configuration
requiredSignatures={requiredSignatures}
authorities={authorities}
symbol={symbol}
maxSingleDeposit={maxSingleDeposit}
maxTotalBalance={maxTotalBalance} />
<div className="status-left-container" />
<div className="status-page-container">
<div className="status-configuration-container">
<span className="status-configuration-title status-title">Configuration</span>
<Configuration
requiredSignatures={requiredSignatures}
authorities={authorities}
symbol={symbol}
maxSingleDeposit={maxSingleDeposit}
maxTotalBalance={maxTotalBalance}
/>
</div>
{withoutEvents ? null :
<div className='status-authorities-container'>
<span className='status-authorities-title status-title'>Authorities</span>
<div className='status-authorities-data'>
{validatorsList.map((validator,i) => (
<Authority key={validator} address={validator} number={(i+1)} logoIndex={(i) % 3} />
{withoutEvents ? null : (
<div className="status-authorities-container">
<span className="status-authorities-title status-title">Authorities</span>
<div className="status-authorities-data">
{validatorsList.map((validator, i) => (
<Authority key={validator} address={validator} number={i + 1} logoIndex={i % 3} />
))}
</div>
</div>
}
)}
</div>
<div className='pattern-background'>
<div className="pattern-background">
<div className="pattern-background-image" />
</div>
</div>

View File

@ -1,13 +1,13 @@
import React from 'react';
import swal from 'sweetalert';
import { inject, observer } from "mobx-react";
import React from 'react'
import swal from 'sweetalert'
import { inject, observer } from 'mobx-react'
@inject("RootStore")
@inject('RootStore')
@observer
export class SweetAlert extends React.Component {
componentWillReact(){
componentWillReact() {
const { alertStore } = this.props.RootStore
if(alertStore.alerts.length > 0){
if (alertStore.alerts.length > 0) {
const alert = alertStore.alerts.slice()[0]
swal(alert).then(() => {
alertStore.remove(alert)
@ -23,10 +23,8 @@ export class SweetAlert extends React.Component {
}
}
render(){
render() {
this.logErrors()
return (
<div style={{display: 'none'}} />
)
return <div style={{ display: 'none' }} />
}
}

View File

@ -4,11 +4,7 @@ import { DataBlock } from './DataBlock'
export const TransactionsStatistics = ({ txNumber, value, type }) => (
<div className="statistics-bridge-data">
<DataBlock
description="Transactions"
value={numeral(txNumber).format('0,0 a')}
type=''
/>
<DataBlock description="Transactions" value={numeral(txNumber).format('0,0 a')} type="" />
<div className="separator" />
<DataBlock
description="Total Value"

View File

@ -14,24 +14,27 @@ export const TransferAlert = ({
toAmount,
fee,
reverse
}) => {
}) => {
const formattedFromAmount = numeral(fromAmount).format('0,0[.][000000000000000000]', Math.floor)
const formattedToAmount = numeral(toAmount).format('0,0[.][000000000000000000]', Math.floor)
return (
<div className="transfer-alert">
<div className="image-container">
<img className="arrows-icon" src={arrowsIcon} alt="transfer icon"/>
<img className="arrows-icon" src={arrowsIcon} alt="transfer icon" />
</div>
<div className="alert-container">
<div className="transfer-title">
<div className="alert-logo-box">
<div className={reverse ? 'foreign-logo' : 'home-logo'} />
</div>
<div><strong>{formattedFromAmount}</strong> {fromCurrency}</div>
<div>
<strong>{formattedFromAmount}</strong> {fromCurrency}
</div>
<ArrowRight />
<div><strong>{formattedToAmount}</strong> {toCurrency}</div>
<div>
<strong>{formattedToAmount}</strong> {toCurrency}
</div>
<div className="alert-logo-box">
<div className={reverse ? 'home-logo' : 'foreign-logo'} />
</div>
@ -39,11 +42,17 @@ export const TransferAlert = ({
<p className="transfer-description" data-testid="transfer-description">
<strong>{fee && `Fee: ${fee.toString()}%`}</strong>
<br />
Please confirm that you would like to send <strong>{formattedFromAmount}</strong> {fromCurrency} from {from} to receive <strong>{formattedToAmount}</strong> {toCurrency} on {to}.
Please confirm that you would like to send <strong>{formattedFromAmount}</strong>{' '}
{fromCurrency} from {from} to receive <strong>{formattedToAmount}</strong> {toCurrency} on{' '}
{to}.
</p>
<div className="transfer-buttons">
<button className="transfer-confirm" onClick={onConfirmation}>Continue</button>
<button className="transfer-cancel" onClick={onCancel}>Cancel</button>
<button className="transfer-confirm" onClick={onConfirmation}>
Continue
</button>
<button className="transfer-cancel" onClick={onCancel}>
Cancel
</button>
</div>
</div>
</div>

View File

@ -1,29 +1,36 @@
import React from 'react';
import { inject, observer } from "mobx-react";
import React from 'react'
import { inject, observer } from 'mobx-react'
import { WalletIcon } from './menu-icons/WalletIcon'
@inject("RootStore")
@inject('RootStore')
@observer
export class Wallet extends React.Component {
render() {
const { web3Store, homeStore, foreignStore, alertStore } = this.props.RootStore
const isHome = web3Store.metamaskNet.id.toString() === web3Store.homeNet.id.toString()
const address = web3Store.defaultAccount.address
const explorerAddressUrl = isHome ? homeStore.getExplorerAddressUrl(address) : foreignStore.getExplorerAddressUrl(address)
const completed = isHome ? homeStore.getDailyQuotaCompleted() : foreignStore.getDailyQuotaCompleted()
const explorerAddressUrl = isHome
? homeStore.getExplorerAddressUrl(address)
: foreignStore.getExplorerAddressUrl(address)
const completed = isHome
? homeStore.getDailyQuotaCompleted()
: foreignStore.getDailyQuotaCompleted()
const width = `${completed}%`
const wallet = web3Store.defaultAccount.address !== '' && web3Store.defaultAccount.address !== undefined
? (<a
href={explorerAddressUrl}
target="_blank"
className="wallet-text wallet-link">
{web3Store.defaultAccount.address.slice(0,17).concat('...')}
</a>)
: (<span className="wallet-text">Login with <span className="wallet-text-metamask">wallet</span></span>)
const wallet =
web3Store.defaultAccount.address !== '' && web3Store.defaultAccount.address !== undefined ? (
<a href={explorerAddressUrl} target="_blank" className="wallet-text wallet-link">
{web3Store.defaultAccount.address.slice(0, 17).concat('...')}
</a>
) : (
<span className="wallet-text">
Login with <span className="wallet-text-metamask">wallet</span>
</span>
)
return (
<div className="header-wallet"
<div
className="header-wallet"
onMouseEnter={() => alertStore.setShowDailyQuotaInfo(true)}
onMouseLeave={() => alertStore.setShowDailyQuotaInfo(false)}
>
@ -32,7 +39,7 @@ export class Wallet extends React.Component {
{wallet}
</div>
<div className="daily-quota-container">
{web3Store.metamaskNet.id && <div className="daily-quota-progress" style={{width}} />}
{web3Store.metamaskNet.id && <div className="daily-quota-progress" style={{ width }} />}
</div>
</div>
)

View File

@ -26,7 +26,8 @@ describe('FeeStatistics', () => {
<FeeStatistics
depositFeeCollected={depositFeeCollected}
withdrawFeeCollected={withdrawFeeCollected}
/>)
/>
)
// Then
const container = queryByTestId('fee-statistics')
@ -63,7 +64,8 @@ describe('FeeStatistics', () => {
<FeeStatistics
depositFeeCollected={depositFeeCollected}
withdrawFeeCollected={withdrawFeeCollected}
/>)
/>
)
// Then
const container = queryByTestId('fee-statistics')
@ -97,7 +99,8 @@ describe('FeeStatistics', () => {
<FeeStatistics
depositFeeCollected={depositFeeCollected}
withdrawFeeCollected={withdrawFeeCollected}
/>)
/>
)
// Then
const container = queryByTestId('fee-statistics')
@ -131,7 +134,8 @@ describe('FeeStatistics', () => {
<FeeStatistics
depositFeeCollected={depositFeeCollected}
withdrawFeeCollected={withdrawFeeCollected}
/>)
/>
)
// Then
const container = queryByTestId('fee-statistics')

View File

@ -6,19 +6,19 @@ import 'jest-dom/extend-expect'
afterEach(cleanup)
const baseData = {
address: "0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26",
balance: "99.99",
currency: "TEST",
address: '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26',
balance: '99.99',
currency: 'TEST',
displayTokenAddress: true,
getExplorerAddressUrl: () => {},
isHome: false,
maxCurrentLimit: "20000",
maxPerTx: "2000",
minPerTx: "0.01",
tokenAddress: "0xb69d9C58C258080eABF499270c778bBDE38dd6Ac",
tokenName: "TEST",
totalSupply: "100",
url: "https://ropsten.infura.io"
maxCurrentLimit: '20000',
maxPerTx: '2000',
minPerTx: '0.01',
tokenAddress: '0xb69d9C58C258080eABF499270c778bBDE38dd6Ac',
tokenName: 'TEST',
totalSupply: '100',
url: 'https://ropsten.infura.io'
}
describe('NetworkDetails', () => {
@ -30,7 +30,7 @@ describe('NetworkDetails', () => {
}
// When
const { queryByTestId } = render(<NetworkDetails {...data}/>)
const { queryByTestId } = render(<NetworkDetails {...data} />)
// Then
const container = queryByTestId('network-details')
@ -46,7 +46,7 @@ describe('NetworkDetails', () => {
}
// When
const { queryByTestId } = render(<NetworkDetails {...data}/>)
const { queryByTestId } = render(<NetworkDetails {...data} />)
// Then
const container = queryByTestId('network-details')

View File

@ -6,7 +6,7 @@ import BN from 'bignumber.js'
afterEach(cleanup)
describe('TransferAlert', function () {
describe('TransferAlert', function() {
it('does not render fee information if not provided', async () => {
const data = {
from: 'Sokol',
@ -19,7 +19,7 @@ describe('TransferAlert', function () {
reverse: false
}
const { getByTestId } = render(<TransferAlert { ...data } />)
const { getByTestId } = render(<TransferAlert {...data} />)
expect(getByTestId('transfer-description')).not.toHaveTextContent('Fee')
})
@ -40,7 +40,7 @@ describe('TransferAlert', function () {
reverse: false
}
const { getByTestId } = render(<TransferAlert { ...data } />)
const { getByTestId } = render(<TransferAlert {...data} />)
expect(getByTestId('transfer-description')).toHaveTextContent('Fee')
expect(getByTestId('transfer-description')).toHaveTextContent(fee.toString())

View File

@ -4,29 +4,44 @@ import numeral from 'numeral'
import { CopyIcon } from '../icons/CopyIcon'
import { CopyToClipboard } from 'react-copy-to-clipboard'
export const Event = ({ color, eventName, transactionHash, recipient, value, blockNumber, txUrl, accountUrl }) => (
export const Event = ({
color,
eventName,
transactionHash,
recipient,
value,
blockNumber,
txUrl,
accountUrl
}) => (
<div>
<div className="event">
<div className="event-tx-container txhash-column">
<span className={`event-name background-${color}`}>{eventName}</span>
<span>
<a href={txUrl} target="_blank" className="event-txhash">{transactionHash.slice(0,18).concat('...')}</a>
<a href={txUrl} target="_blank" className="event-txhash">
{transactionHash.slice(0, 18).concat('...')}
</a>
<CopyToClipboard text={transactionHash}>
<span className="copy-icon copy-icon-right"><CopyIcon /></span>
<span className="copy-icon copy-icon-right">
<CopyIcon />
</span>
</CopyToClipboard>
</span>
</div>
<a href={accountUrl} target="_blank" className="event-recipient recipient-column">
{recipient ? <strong className="only-mobile event-recipient-label ">Recipient</strong> : ''}
{recipient ? recipient.slice(0,27).concat('...') : ''}
{recipient ? recipient.slice(0, 27).concat('...') : ''}
</a>
<span className="event-value value-column">
{recipient ? <strong className="only-mobile">Value</strong> : ''}
{value ? numeral(fromWei(value)).format('0,0.00', Math.floor) : ''}
</span>
<span className="event-block block-column"><strong className="only-mobile">Block</strong>{blockNumber}</span>
<span className="event-block block-column">
<strong className="only-mobile">Block</strong>
{blockNumber}
</span>
</div>
<div className="event-separator" />
</div>
)

View File

@ -1,13 +1,11 @@
import React from 'react';
import React from 'react'
export const EventHeader = ({color, eventName, transactionHash, handleClick}) => (
export const EventHeader = ({ color, eventName, transactionHash, handleClick }) => (
<div className="events-i-header">
<div className="events-i-header-title">
<p className={`label ${color}`}>{eventName}</p>
</div>
<p className="description break-all">
tx: {transactionHash}
</p>
<p className="description break-all">tx: {transactionHash}</p>
<div onClick={handleClick} className="events-i-switcher" />
</div>
)

View File

@ -1,4 +1,4 @@
import React from "react";
import React from 'react'
export const ArrowRight = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="10">
@ -7,4 +7,4 @@ export const ArrowRight = () => (
d="M14 5c0 .01-.005.017-.005.027a.956.956 0 0 1-.28.688c-.063.063-.139.099-.213.14l-3.86 3.86a.959.959 0 1 1-1.357-1.357L10.642 6H1a1 1 0 0 1 0-2h9.642L8.285 1.642A.959.959 0 1 1 9.642.285l3.856 3.856c.076.042.153.08.217.144a.956.956 0 0 1 .279.692c0 .009.006.015.006.023z"
/>
</svg>
);
)

View File

@ -1,7 +1,10 @@
import React from "react"
import React from 'react'
export const CopyIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14">
<path fillRule="evenodd" d="M13 10a1 1 0 0 1-1-1V2H5a1 1 0 0 1 0-2h8a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1zm-3-5v8a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1zM8 6H2v6h6V6z"/>
<path
fillRule="evenodd"
d="M13 10a1 1 0 0 1-1-1V2H5a1 1 0 0 1 0-2h8a1 1 0 0 1 1 1v8a1 1 0 0 1-1 1zm-3-5v8a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h8a1 1 0 0 1 1 1zM8 6H2v6h6V6z"
/>
</svg>
)

View File

@ -1,7 +1,10 @@
import React from "react"
import React from 'react'
export const InfoIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
<path fillRule="evenodd" d="M12 24C5.373 24 0 18.627 0 12S5.373 0 12 0s12 5.373 12 12-5.373 12-12 12zm0-22C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm0 16a1 1 0 0 1-1-1v-6a1 1 0 0 1 2 0v6a1 1 0 0 1-1 1zm0-10a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
<path
fillRule="evenodd"
d="M12 24C5.373 24 0 18.627 0 12S5.373 0 12 0s12 5.373 12 12-5.373 12-12 12zm0-22C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm0 16a1 1 0 0 1-1-1v-6a1 1 0 0 1 2 0v6a1 1 0 0 1-1 1zm0-10a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"
/>
</svg>
)

View File

@ -1,9 +1,9 @@
export { Header } from './Header';
export { Bridge } from './Bridge';
export { RelayEvents } from './RelayEvents';
export { Footer } from './Footer';
export { SweetAlert } from './SweetAlert';
export { Loading } from './Loading';
export { Header } from './Header'
export { Bridge } from './Bridge'
export { RelayEvents } from './RelayEvents'
export { Footer } from './Footer'
export { SweetAlert } from './SweetAlert'
export { Loading } from './Loading'
export { Fade } from './Fade'
export { BridgeForm } from './BridgeForm'
export { BridgeNetwork } from './BridgeNetwork'

View File

@ -1,4 +1,4 @@
import React from "react"
import React from 'react'
export const EventsIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="17">

View File

@ -1,7 +1,10 @@
import React from "react"
import React from 'react'
export const MobileMenuCloseIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<path fillRule="evenodd" d="M17.691 1.719l-3 3-4.265 4.265.019.02 4.245 4.245 3 3a1.019 1.019 0 1 1-1.441 1.441l-3-3-4.265-4.264L4.72 14.69l-3 3a1.02 1.02 0 0 1-1.442-1.441l3-3 4.265-4.265-.02-.019-4.245-4.246-3-3A1.019 1.019 0 1 1 1.719.278l3 3 4.265 4.265 4.266-4.265 3-3a1.019 1.019 0 1 1 1.441 1.441z"/>
<path
fillRule="evenodd"
d="M17.691 1.719l-3 3-4.265 4.265.019.02 4.245 4.245 3 3a1.019 1.019 0 1 1-1.441 1.441l-3-3-4.265-4.264L4.72 14.69l-3 3a1.02 1.02 0 0 1-1.442-1.441l3-3 4.265-4.265-.02-.019-4.245-4.246-3-3A1.019 1.019 0 1 1 1.719.278l3 3 4.265 4.265 4.266-4.265 3-3a1.019 1.019 0 1 1 1.441 1.441z"
/>
</svg>
)

View File

@ -1,7 +1,10 @@
import React from "react"
import React from 'react'
export const MobileMenuIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="14">
<path fillRule="evenodd" d="M17 8H1a1 1 0 0 1 0-2h16a1 1 0 0 1 0 2zm0-6H1a1 1 0 0 1 0-2h16a1 1 0 0 1 0 2zM1 12h16a1 1 0 0 1 0 2H1a1 1 0 0 1 0-2z"/>
<path
fillRule="evenodd"
d="M17 8H1a1 1 0 0 1 0-2h16a1 1 0 0 1 0 2zm0-6H1a1 1 0 0 1 0-2h16a1 1 0 0 1 0 2zM1 12h16a1 1 0 0 1 0 2H1a1 1 0 0 1 0-2z"
/>
</svg>
)

View File

@ -1,4 +1,4 @@
import React from "react"
import React from 'react'
export const StatisticsIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">

View File

@ -1,4 +1,4 @@
import React from "react"
import React from 'react'
export const StatusIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">

View File

@ -1,7 +1,10 @@
import React from "react"
import React from 'react'
export const WalletIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18">
<path fillRule="evenodd" d="M17 18H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3h3a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1zM12 2H2v2h10V2zm4 4H2v10h14V6zm-3 4a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
<path
fillRule="evenodd"
d="M17 18H1a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3h3a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1zM12 2H2v2h10V2zm4 4H2v10h14V6zm-3 4a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"
/>
</svg>
)

View File

@ -1,5 +1,5 @@
export * from './EventsIcon';
export * from './MobileMenuCloseIcon';
export * from './MobileMenuIcon';
export * from './StatisticsIcon';
export * from './StatusIcon';
export * from './EventsIcon'
export * from './MobileMenuCloseIcon'
export * from './MobileMenuIcon'
export * from './StatisticsIcon'
export * from './StatusIcon'

View File

@ -2,14 +2,11 @@ import React from 'react'
export const IconGithub = () => {
return (
<svg
viewBox="0 0 16 16"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
d="M14.928 4.084a8.067 8.067 0 0 0-2.912-2.985A7.673 7.673 0 0 0 8-.001a7.67 7.67 0 0 0-4.016 1.1 8.06 8.06 0 0 0-2.912 2.985C.356 5.34-.001 6.712-.001 8.201c0 1.787.508 3.394 1.526 4.821 1.017 1.428 2.332 2.416 3.943 2.964.188.035.327.01.417-.075a.422.422 0 0 0 .135-.32l-.005-.577c-.003-.363-.005-.68-.005-.95l-.24.042a2.985 2.985 0 0 1-.578.038 4.418 4.418 0 0 1-.724-.075 1.6 1.6 0 0 1-.698-.32 1.359 1.359 0 0 1-.458-.657l-.105-.246a2.64 2.64 0 0 0-.328-.544c-.149-.2-.3-.335-.453-.406l-.073-.053a.78.78 0 0 1-.135-.129.572.572 0 0 1-.094-.15c-.021-.049-.004-.09.052-.122.056-.032.156-.048.302-.048l.209.032c.138.028.31.113.515.256.205.142.374.328.506.555.159.292.352.515.578.668.225.153.453.229.682.229.229 0 .427-.017.594-.053.167-.036.323-.089.469-.16.062-.477.232-.844.51-1.1a6.938 6.938 0 0 1-1.068-.193 4.188 4.188 0 0 1-.979-.416 2.828 2.828 0 0 1-.839-.716c-.222-.284-.404-.658-.547-1.121-.142-.463-.213-.997-.213-1.602 0-.862.274-1.595.823-2.2-.257-.648-.233-1.374.073-2.179.201-.064.5-.016.896.144.396.161.686.298.87.412.184.113.332.21.443.288a7.225 7.225 0 0 1 2-.278c.687 0 1.354.093 2.001.278l.395-.256a5.53 5.53 0 0 1 .959-.47c.368-.142.649-.182.844-.117.312.804.34 1.53.083 2.178.549.605.823 1.338.823 2.2 0 .605-.071 1.141-.213 1.607-.143.467-.327.84-.553 1.122a2.926 2.926 0 0 1-.843.71 4.194 4.194 0 0 1-.98.416c-.316.086-.672.15-1.068.193.362.32.542.826.542 1.516v2.253c0 .129.044.235.13.321.087.085.224.11.412.074 1.611-.548 2.926-1.536 3.943-2.963 1.018-1.427 1.526-3.035 1.526-4.822 0-1.488-.358-2.86-1.073-4.116z"
/>
</svg>
<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
d="M14.928 4.084a8.067 8.067 0 0 0-2.912-2.985A7.673 7.673 0 0 0 8-.001a7.67 7.67 0 0 0-4.016 1.1 8.06 8.06 0 0 0-2.912 2.985C.356 5.34-.001 6.712-.001 8.201c0 1.787.508 3.394 1.526 4.821 1.017 1.428 2.332 2.416 3.943 2.964.188.035.327.01.417-.075a.422.422 0 0 0 .135-.32l-.005-.577c-.003-.363-.005-.68-.005-.95l-.24.042a2.985 2.985 0 0 1-.578.038 4.418 4.418 0 0 1-.724-.075 1.6 1.6 0 0 1-.698-.32 1.359 1.359 0 0 1-.458-.657l-.105-.246a2.64 2.64 0 0 0-.328-.544c-.149-.2-.3-.335-.453-.406l-.073-.053a.78.78 0 0 1-.135-.129.572.572 0 0 1-.094-.15c-.021-.049-.004-.09.052-.122.056-.032.156-.048.302-.048l.209.032c.138.028.31.113.515.256.205.142.374.328.506.555.159.292.352.515.578.668.225.153.453.229.682.229.229 0 .427-.017.594-.053.167-.036.323-.089.469-.16.062-.477.232-.844.51-1.1a6.938 6.938 0 0 1-1.068-.193 4.188 4.188 0 0 1-.979-.416 2.828 2.828 0 0 1-.839-.716c-.222-.284-.404-.658-.547-1.121-.142-.463-.213-.997-.213-1.602 0-.862.274-1.595.823-2.2-.257-.648-.233-1.374.073-2.179.201-.064.5-.016.896.144.396.161.686.298.87.412.184.113.332.21.443.288a7.225 7.225 0 0 1 2-.278c.687 0 1.354.093 2.001.278l.395-.256a5.53 5.53 0 0 1 .959-.47c.368-.142.649-.182.844-.117.312.804.34 1.53.083 2.178.549.605.823 1.338.823 2.2 0 .605-.071 1.141-.213 1.607-.143.467-.327.84-.553 1.122a2.926 2.926 0 0 1-.843.71 4.194 4.194 0 0 1-.98.416c-.316.086-.672.15-1.068.193.362.32.542.826.542 1.516v2.253c0 .129.044.235.13.321.087.085.224.11.412.074 1.611-.548 2.926-1.536 3.943-2.963 1.018-1.427 1.526-3.035 1.526-4.822 0-1.488-.358-2.86-1.073-4.116z"
/>
</svg>
)
}

View File

@ -2,10 +2,7 @@ import React from 'react'
export const IconPOA = () => {
return (
<svg
viewBox="0 0 125 40"
xmlns="http://www.w3.org/2000/svg"
>
<svg viewBox="0 0 125 40" xmlns="http://www.w3.org/2000/svg">
<g fillRule="evenodd">
<path d="M61.4 0C50.482 0 41.6 8.883 41.6 19.8c0 10.918 8.882 19.8 19.8 19.8 10.918 0 19.8-8.882 19.8-19.8C81.2 8.883 72.318 0 61.4 0M21.206 31.718h2.505c8.06 0 14.949-6.26 15.24-14.323C39.254 8.95 32.476 1.98 24.103 1.98H3.127C2.504 1.98 2 2.485 2 3.108v34.044c0 .622.504 1.128 1.127 1.128H18.88c.615 0 1.117-.494 1.127-1.11l.073-4.343a1.127 1.127 0 0 1 1.126-1.109M98.002 1.271l-21.295 34.65c-.388.745.16 1.629 1.014 1.632l17.47.067 25.129-.067c.852-.003 1.401-.887 1.013-1.632l-21.294-34.65c-.425-.815-1.612-.815-2.037 0" />
</g>

View File

@ -2,10 +2,7 @@ import React from 'react'
export const IconTelegram = () => {
return (
<svg
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path
clipRule="evenodd"
d="M18.384,22.779c0.322,0.228 0.737,0.285 1.107,0.145c0.37,-0.141 0.642,-0.457 0.724,-0.84c0.869,-4.084 2.977,-14.421 3.768,-18.136c0.06,-0.28 -0.04,-0.571 -0.26,-0.758c-0.22,-0.187 -0.525,-0.241 -0.797,-0.14c-4.193,1.552 -17.106,6.397 -22.384,8.35c-0.335,0.124 -0.553,0.446 -0.542,0.799c0.012,0.354 0.25,0.661 0.593,0.764c2.367,0.708 5.474,1.693 5.474,1.693c0,0 1.452,4.385 2.209,6.615c0.095,0.28 0.314,0.5 0.603,0.576c0.288,0.075 0.596,-0.004 0.811,-0.207c1.216,-1.148 3.096,-2.923 3.096,-2.923c0,0 3.572,2.619 5.598,4.062Zm-11.01,-8.677l1.679,5.538l0.373,-3.507c0,0 6.487,-5.851 10.185,-9.186c0.108,-0.098 0.123,-0.262 0.033,-0.377c-0.089,-0.115 -0.253,-0.142 -0.376,-0.064c-4.286,2.737 -11.894,7.596 -11.894,7.596Z"

View File

@ -2,10 +2,7 @@ import React from 'react'
export const IconTwitter = () => {
return (
<svg
viewBox="0 0 16 12"
xmlns="http://www.w3.org/2000/svg"
>
<svg viewBox="0 0 16 12" xmlns="http://www.w3.org/2000/svg">
<path
d="M15.086.222a6.236 6.236 0 0 1-1.955.735A3.089 3.089 0 0 0 10.885 0c-1.7 0-3.077 1.357-3.077 3.03 0 .237.027.469.079.69A8.781 8.781 0 0 1 1.545.554a2.974 2.974 0 0 0-.416 1.523c0 1.052.544 1.979 1.368 2.522a3.117 3.117 0 0 1-1.393-.381v.038c0 1.468 1.061 2.692 2.468 2.972a3.18 3.18 0 0 1-1.39.051 3.074 3.074 0 0 0 2.874 2.105 6.24 6.24 0 0 1-3.822 1.296c-.248 0-.493-.014-.734-.041A8.814 8.814 0 0 0 5.217 12c5.66 0 8.755-4.617 8.755-8.621l-.01-.393A6.153 6.153 0 0 0 15.5 1.42a6.246 6.246 0 0 1-1.767.477A3.048 3.048 0 0 0 15.086.222z"
fillRule="evenodd"

View File

@ -1,4 +1,4 @@
export * from './IconGithub';
export * from './IconPOA';
export * from './IconTelegram';
export * from './IconTwitter';
export * from './IconGithub'
export * from './IconPOA'
export * from './IconTelegram'
export * from './IconTwitter'

View File

@ -4,6 +4,6 @@ export const setItem = (key, data) => {
localStorage.setItem(key, data)
}
export const getItem = (key) => {
export const getItem = key => {
return localStorage.getItem(key)
}

View File

@ -1,15 +1,15 @@
const yn = input => {
input = String(input).trim();
input = String(input).trim()
if (/^(?:y|yes|true|1)$/i.test(input)) {
return true;
return true
}
if (/^(?:n|no|false|0)$/i.test(input)) {
return false;
return false
}
return false;
};
return false
}
export default yn

View File

@ -1,14 +1,15 @@
import React from 'react';
import ReactDOM from 'react-dom';
import { App } from './App';
import React from 'react'
import ReactDOM from 'react-dom'
import { App } from './App'
import { BrowserRouter } from 'react-router-dom'
import { Provider } from "mobx-react";
import RootStore from './stores/RootStore';
import { Provider } from 'mobx-react'
import RootStore from './stores/RootStore'
ReactDOM.render((
ReactDOM.render(
<Provider RootStore={RootStore}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>
), document.getElementById('root'));
</Provider>,
document.getElementById('root')
)

View File

@ -1,9 +1,9 @@
import { action, observable } from "mobx";
import { action, observable } from 'mobx'
class AlertStore {
@observable alerts = [];
@observable showLoading = false;
@observable loadingStepIndex = -1;
@observable alerts = []
@observable showLoading = false
@observable loadingStepIndex = -1
@observable blockConfirmations = 0
@observable showDailyQuotaInfo = false
homeConnectionErrorSended = false
@ -14,39 +14,39 @@ class AlertStore {
'Waiting for Block Confirmations...',
'Validators Verifying Transaction...',
'Transfer Complete'
];
]
HOME_CONNECTION_ERROR = 'Home Connection Error'
FOREIGN_CONNECTION_ERROR = 'Foreign Connection Error'
HOME_TRANSFER_SUCCESS = 'Home Transfer Success'
FOREIGN_TRANSFER_SUCCESS = 'Foreign Transfer Success'
@action
pushError(message, messageType){
console.error("Error: ", message)
pushError(message, messageType) {
console.error('Error: ', message)
const shouldPushError = this.checkErrorPush(messageType, messageType)
if(shouldPushError) {
const node = document.createElement("div")
if (shouldPushError) {
const node = document.createElement('div')
node.innerHTML = message
const error = {
title: "Error",
title: 'Error',
content: node,
icon: "error",
icon: 'error',
messageType
}
this.alerts.push(error)
}
}
checkErrorPush(message, messageType){
if(messageType === this.HOME_CONNECTION_ERROR) {
if(this.homeConnectionErrorSended) {
checkErrorPush(message, messageType) {
if (messageType === this.HOME_CONNECTION_ERROR) {
if (this.homeConnectionErrorSended) {
return false
} else {
this.homeConnectionErrorSended = true
return true
}
} else if(messageType === this.FOREIGN_CONNECTION_ERROR) {
if(this.foreignConnectionErrorSended) {
} else if (messageType === this.FOREIGN_CONNECTION_ERROR) {
if (this.foreignConnectionErrorSended) {
return false
} else {
this.foreignConnectionErrorSended = true
@ -55,31 +55,30 @@ class AlertStore {
} else {
return true
}
}
@action
pushSuccess(message, messageType){
const node = document.createElement("div")
pushSuccess(message, messageType) {
const node = document.createElement('div')
node.innerHTML = message
const success = {
title: "Success",
title: 'Success',
content: node,
icon: "success",
icon: 'success',
messageType
}
this.alerts.push(success)
}
remove(value){
const result = this.alerts.remove(value);
remove(value) {
const result = this.alerts.remove(value)
console.log(result, value, this.alerts)
}
@action
setLoading(status) {
this.showLoading = status;
this.loadingStepIndex = 0;
this.showLoading = status
this.loadingStepIndex = 0
this.blockConfirmations = 0
}
@ -90,10 +89,12 @@ class AlertStore {
@action
setLoadingStepIndex(index) {
this.loadingStepIndex = index;
this.loadingStepIndex = index
console.log(this.loadingSteps[index])
if(index === this.loadingSteps.length - 1) {
setTimeout(() => { this.setLoading(false)}, 2000)
if (index === this.loadingSteps.length - 1) {
setTimeout(() => {
this.setLoading(false)
}, 2000)
}
}
@ -105,7 +106,6 @@ class AlertStore {
setShowDailyQuotaInfo(value) {
this.showDailyQuotaInfo = value
}
}
export default AlertStore;
export default AlertStore

View File

@ -1,5 +1,5 @@
import { action, observable } from 'mobx';
import { abi as ERC677_ABI } from '../contracts/ERC677BridgeToken.json';
import { action, observable } from 'mobx'
import { abi as ERC677_ABI } from '../contracts/ERC677BridgeToken.json'
import { getBlockNumber } from './utils/web3'
import {
getMaxPerTxLimit,
@ -23,65 +23,71 @@ import {
} from './utils/contract'
import { balanceLoaded, removePendingTransaction } from './utils/testUtils'
import sleep from './utils/sleep'
import { getBridgeABIs, getUnit, BRIDGE_MODES, decodeFeeManagerMode, FEE_MANAGER_MODE } from './utils/bridgeMode'
import {
getBridgeABIs,
getUnit,
BRIDGE_MODES,
decodeFeeManagerMode,
FEE_MANAGER_MODE
} from './utils/bridgeMode'
import { abi as BRIDGE_VALIDATORS_ABI } from '../contracts/BridgeValidators'
import ERC20Bytes32Abi from './utils/ERC20Bytes32.abi'
import BN from 'bignumber.js'
import { processLargeArrayAsync } from "./utils/array"
import { fromDecimals } from "./utils/decimals"
import { processLargeArrayAsync } from './utils/array'
import { fromDecimals } from './utils/decimals'
class ForeignStore {
@observable state = null;
@observable loading = true;
@observable events = [];
@observable totalSupply = '';
@observable symbol = 'NOSYM';
@observable tokenName = '';
@observable balance = '';
@observable filter = false;
@observable maxCurrentDeposit = '';
@observable maxPerTx = '';
@observable minPerTx = '';
@observable latestBlockNumber = 0;
@observable state = null
@observable loading = true
@observable events = []
@observable totalSupply = ''
@observable symbol = 'NOSYM'
@observable tokenName = ''
@observable balance = ''
@observable filter = false
@observable maxCurrentDeposit = ''
@observable maxPerTx = ''
@observable minPerTx = ''
@observable latestBlockNumber = 0
@observable validators = []
@observable validatorsCount = 0
@observable foreignBridgeValidators = ''
@observable requiredSignatures = 0
@observable dailyLimit = 0
@observable totalSpentPerDay = 0
@observable tokenAddress = '';
@observable tokenAddress = ''
@observable feeEventsFinished = false
@observable tokenType = ''
feeManager = {
totalFeeDistributedFromSignatures: BN(0),
totalFeeDistributedFromAffirmation: BN(0)
};
}
networkName = process.env.REACT_APP_FOREIGN_NETWORK_NAME || 'Unknown'
filteredBlockNumber = 0;
foreignBridge = {};
filteredBlockNumber = 0
foreignBridge = {}
tokenContract = {}
tokenDecimals = 18;
FOREIGN_BRIDGE_ADDRESS = process.env.REACT_APP_FOREIGN_BRIDGE_ADDRESS;
tokenDecimals = 18
FOREIGN_BRIDGE_ADDRESS = process.env.REACT_APP_FOREIGN_BRIDGE_ADDRESS
explorerTxTemplate = process.env.REACT_APP_FOREIGN_EXPLORER_TX_TEMPLATE || ''
explorerAddressTemplate = process.env.REACT_APP_FOREIGN_EXPLORER_ADDRESS_TEMPLATE || ''
constructor (rootStore) {
this.web3Store = rootStore.web3Store;
constructor(rootStore) {
this.web3Store = rootStore.web3Store
this.foreignWeb3 = rootStore.web3Store.foreignWeb3
this.alertStore = rootStore.alertStore
this.homeStore = rootStore.homeStore;
this.homeStore = rootStore.homeStore
this.rootStore = rootStore
this.waitingForConfirmation = new Set()
this.setForeign()
}
async setForeign(){
async setForeign() {
if (!this.rootStore.bridgeModeInitialized) {
setTimeout(() => this.setForeign(), 200)
return
}
const { FOREIGN_ABI } = getBridgeABIs(this.rootStore.bridgeMode)
this.foreignBridge = new this.foreignWeb3.eth.Contract(FOREIGN_ABI, this.FOREIGN_BRIDGE_ADDRESS);
this.foreignBridge = new this.foreignWeb3.eth.Contract(FOREIGN_ABI, this.FOREIGN_BRIDGE_ADDRESS)
await this.getBlockNumber()
await this.getTokenInfo()
this.getMinPerTxLimit()
@ -104,65 +110,73 @@ class ForeignStore {
async getBlockNumber() {
try {
this.latestBlockNumber = await getBlockNumber(this.foreignWeb3)
} catch(e){
} catch (e) {
console.error(e)
}
}
@action
async getMaxPerTxLimit(){
async getMaxPerTxLimit() {
try {
this.maxPerTx = await getMaxPerTxLimit(this.foreignBridge,this.tokenDecimals)
} catch(e){
this.maxPerTx = await getMaxPerTxLimit(this.foreignBridge, this.tokenDecimals)
} catch (e) {
console.error(e)
}
}
@action
async getMinPerTxLimit(){
async getMinPerTxLimit() {
try {
this.minPerTx = await getMinPerTxLimit(this.foreignBridge,this.tokenDecimals)
} catch(e){
this.minPerTx = await getMinPerTxLimit(this.foreignBridge, this.tokenDecimals)
} catch (e) {
console.error(e)
}
}
@action
async getTokenInfo(){
async getTokenInfo() {
try {
this.tokenAddress = this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_ERC || this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE
? await getErc20TokenAddress(this.foreignBridge)
: await getErc677TokenAddress(this.foreignBridge)
this.tokenContract = new this.foreignWeb3.eth.Contract(ERC677_ABI, this.tokenAddress);
this.tokenAddress =
this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_ERC ||
this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE
? await getErc20TokenAddress(this.foreignBridge)
: await getErc677TokenAddress(this.foreignBridge)
this.tokenContract = new this.foreignWeb3.eth.Contract(ERC677_ABI, this.tokenAddress)
this.tokenType = await getTokenType(this.tokenContract, this.FOREIGN_BRIDGE_ADDRESS)
const alternativeContract = new this.foreignWeb3.eth.Contract(ERC20Bytes32Abi, this.tokenAddress);
const alternativeContract = new this.foreignWeb3.eth.Contract(
ERC20Bytes32Abi,
this.tokenAddress
)
try {
this.symbol =await getSymbol(this.tokenContract)
} catch(e) {
this.symbol = this.foreignWeb3.utils.hexToAscii(await getSymbol(alternativeContract)).replace(/\u0000*$/, '')
this.symbol = await getSymbol(this.tokenContract)
} catch (e) {
this.symbol = this.foreignWeb3.utils
.hexToAscii(await getSymbol(alternativeContract))
.replace(/\u0000*$/, '')
}
try {
this.tokenName = await getName(this.tokenContract)
} catch(e) {
this.tokenName = this.foreignWeb3.utils.hexToAscii(await getName(alternativeContract)).replace(/\u0000*$/, '')
} catch (e) {
this.tokenName = this.foreignWeb3.utils
.hexToAscii(await getName(alternativeContract))
.replace(/\u0000*$/, '')
}
this.tokenDecimals = await getDecimals(this.tokenContract)
} catch(e) {
} catch (e) {
console.error(e)
}
}
@action
async getTokenBalance(){
async getTokenBalance() {
try {
this.totalSupply = await getTotalSupply(this.tokenContract)
this.web3Store.getWeb3Promise.then(async () => {
this.balance = await getBalanceOf(this.tokenContract, this.web3Store.defaultAccount.address)
balanceLoaded()
})
} catch(e) {
} catch (e) {
console.error(e)
}
}
@ -174,74 +188,88 @@ class ForeignStore {
const feeManagerModeHash = await getFeeManagerMode(this.foreignBridge)
this.feeManager.feeManagerMode = decodeFeeManagerMode(feeManagerModeHash)
if(this.feeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION) {
this.feeManager.foreignFee = new BN(0);
if (this.feeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION) {
this.feeManager.foreignFee = new BN(0)
this.feeManager.homeFee = await getHomeFee(this.foreignBridge)
}
} else {
this.feeManager.feeManagerMode = FEE_MANAGER_MODE.UNDEFINED
this.feeManager.homeFee = new BN(0);
this.feeManager.foreignFee = new BN(0);
this.feeManager.homeFee = new BN(0)
this.feeManager.foreignFee = new BN(0)
}
}
@action
async getEvents(fromBlock, toBlock) {
try {
fromBlock = fromBlock || this.filteredBlockNumber || this.latestBlockNumber - 50
toBlock = toBlock || this.filteredBlockNumber || "latest"
toBlock = toBlock || this.filteredBlockNumber || 'latest'
if (fromBlock < 0) {
fromBlock = 0
}
let foreignEvents = await getPastEvents(this.foreignBridge, fromBlock, toBlock).catch(e => {
console.error('Couldn\'t get events', e)
console.error("Couldn't get events", e)
return []
})
if(!this.filter){
this.events = foreignEvents;
if (!this.filter) {
this.events = foreignEvents
}
if(this.waitingForConfirmation.size) {
const confirmationEvents = foreignEvents.filter((event) => event.event === "RelayedMessage" && this.waitingForConfirmation.has(event.returnValues.transactionHash))
if (this.waitingForConfirmation.size) {
const confirmationEvents = foreignEvents.filter(
event =>
event.event === 'RelayedMessage' &&
this.waitingForConfirmation.has(event.returnValues.transactionHash)
)
confirmationEvents.forEach(async event => {
const TxReceipt = await this.getTxReceipt(event.transactionHash)
if(TxReceipt && TxReceipt.logs && TxReceipt.logs.length > 1 && this.waitingForConfirmation.size) {
if (
TxReceipt &&
TxReceipt.logs &&
TxReceipt.logs.length > 1 &&
this.waitingForConfirmation.size
) {
this.alertStore.setLoadingStepIndex(3)
const urlExplorer = this.getExplorerTxUrl(event.transactionHash)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitForeign
setTimeout(() => {
this.alertStore.pushSuccess(`${unitReceived} received on ${this.networkName} on Tx
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.networkName} on Tx
<a href='${urlExplorer}' target='blank' style="overflow-wrap: break-word;word-wrap: break-word;">
${event.transactionHash}</a>`, this.alertStore.FOREIGN_TRANSFER_SUCCESS)}
, 2000)
${event.transactionHash}</a>`,
this.alertStore.FOREIGN_TRANSFER_SUCCESS
)
}, 2000)
this.waitingForConfirmation.delete(event.returnValues.transactionHash)
}
})
if(confirmationEvents.length) {
if (confirmationEvents.length) {
removePendingTransaction()
}
}
return foreignEvents
} catch(e) {
this.alertStore.pushError(`Cannot establish connection to Foreign Network.\n
Please make sure you have set it up in env variables`, this.alertStore.FOREIGN_CONNECTION_ERROR)
} catch (e) {
this.alertStore.pushError(
`Cannot establish connection to Foreign Network.\n
Please make sure you have set it up in env variables`,
this.alertStore.FOREIGN_CONNECTION_ERROR
)
}
}
@action
async getCurrentLimit(){
async getCurrentLimit() {
try {
const result = await getCurrentLimit(this.foreignBridge,this.tokenDecimals)
const result = await getCurrentLimit(this.foreignBridge, this.tokenDecimals)
this.maxCurrentDeposit = result.maxCurrentDeposit
this.dailyLimit = result.dailyLimit
this.totalSpentPerDay = result.totalSpentPerDay
} catch(e){
} catch (e) {
console.error(e)
}
}
@ -263,22 +291,23 @@ class ForeignStore {
const txReceipt = await this.getTxReceipt(transactionHash)
const from = txReceipt.blockNumber - 20
const to = txReceipt.blockNumber + 20
const events = await this.getEvents(from, to);
this.events = events.filter((event) => event.transactionHash === transactionHash || event.signedTxHash === transactionHash)
const events = await this.getEvents(from, to)
this.events = events.filter(
event => event.transactionHash === transactionHash || event.signedTxHash === transactionHash
)
} catch (e) {
this.events = []
}
}
@action
async setBlockFilter(blockNumber){
async setBlockFilter(blockNumber) {
this.filteredBlockNumber = blockNumber
this.events = await this.getEvents()
}
@action
setFilter(value){
setFilter(value) {
this.filter = value
}
@ -293,7 +322,7 @@ class ForeignStore {
}
getDailyQuotaCompleted() {
return this.dailyLimit ? this.totalSpentPerDay / this.dailyLimit * 100 : 0
return this.dailyLimit ? (this.totalSpentPerDay / this.dailyLimit) * 100 : 0
}
async waitUntilProcessed(txHash) {
@ -317,44 +346,50 @@ class ForeignStore {
}
@action
async getValidators(){
async getValidators() {
try {
const foreignValidatorsAddress = await this.foreignBridge.methods.validatorContract().call()
this.foreignBridgeValidators = new this.foreignWeb3.eth.Contract(BRIDGE_VALIDATORS_ABI, foreignValidatorsAddress);
this.foreignBridgeValidators = new this.foreignWeb3.eth.Contract(
BRIDGE_VALIDATORS_ABI,
foreignValidatorsAddress
)
this.requiredSignatures = await this.foreignBridgeValidators.methods.requiredSignatures().call()
this.requiredSignatures = await this.foreignBridgeValidators.methods
.requiredSignatures()
.call()
this.validatorsCount = await this.foreignBridgeValidators.methods.validatorCount().call()
this.validators = await getValidatorList(foreignValidatorsAddress, this.foreignWeb3.eth)
} catch(e){
} catch (e) {
console.error(e)
}
}
async getFeeEvents() {
try {
const deployedAtBlock = await getDeployedAtBlock(this.foreignBridge);
const deployedAtBlock = await getDeployedAtBlock(this.foreignBridge)
const events = await getPastEvents(this.foreignBridge, deployedAtBlock, 'latest')
processLargeArrayAsync(
events,
this.processEvent,
() => {
this.feeEventsFinished = true
})
} catch(e){
processLargeArrayAsync(events, this.processEvent, () => {
this.feeEventsFinished = true
})
} catch (e) {
console.error(e)
this.getFeeEvents()
}
}
processEvent = (event) => {
if (event.event === "FeeDistributedFromSignatures") {
this.feeManager.totalFeeDistributedFromSignatures = this.feeManager.totalFeeDistributedFromSignatures.plus(BN(fromDecimals(event.returnValues.feeAmount, this.tokenDecimals)))
} else if (event.event === "FeeDistributedFromAffirmation") {
this.feeManager.totalFeeDistributedFromAffirmation = this.feeManager.totalFeeDistributedFromAffirmation.plus(BN(fromDecimals(event.returnValues.feeAmount, this.tokenDecimals)))
processEvent = event => {
if (event.event === 'FeeDistributedFromSignatures') {
this.feeManager.totalFeeDistributedFromSignatures = this.feeManager.totalFeeDistributedFromSignatures.plus(
BN(fromDecimals(event.returnValues.feeAmount, this.tokenDecimals))
)
} else if (event.event === 'FeeDistributedFromAffirmation') {
this.feeManager.totalFeeDistributedFromAffirmation = this.feeManager.totalFeeDistributedFromAffirmation.plus(
BN(fromDecimals(event.returnValues.feeAmount, this.tokenDecimals))
)
}
}
}
export default ForeignStore;
export default ForeignStore

View File

@ -1,5 +1,5 @@
import { observable, computed } from "mobx";
import { toHex } from 'web3-utils';
import { observable, computed } from 'mobx'
import { toHex } from 'web3-utils'
import { fetchGasPrice, fetchGasPriceFromOracle } from './utils/gas'
const HOME_GAS_PRICE_FALLBACK = process.env.REACT_APP_HOME_GAS_PRICE_FALLBACK
@ -58,4 +58,4 @@ class GasPriceStore {
}
}
export default GasPriceStore;
export default GasPriceStore

View File

@ -1,4 +1,4 @@
import { action, observable } from 'mobx';
import { action, observable } from 'mobx'
import { abi as BRIDGE_VALIDATORS_ABI } from '../contracts/BridgeValidators.json'
import { abi as ERC677_ABI } from '../contracts/ERC677BridgeToken.json'
import { abi as BLOCK_REWARD_ABI } from '../contracts/IBlockReward'
@ -29,10 +29,16 @@ import {
import { balanceLoaded, removePendingTransaction } from './utils/testUtils'
import sleep from './utils/sleep'
import BN from 'bignumber.js'
import { getBridgeABIs, getUnit, BRIDGE_MODES, decodeFeeManagerMode, FEE_MANAGER_MODE } from './utils/bridgeMode'
import {
getBridgeABIs,
getUnit,
BRIDGE_MODES,
decodeFeeManagerMode,
FEE_MANAGER_MODE
} from './utils/bridgeMode'
import ERC20Bytes32Abi from './utils/ERC20Bytes32.abi'
import { processLargeArrayAsync } from './utils/array'
import { getRewardableData } from "./utils/rewardable"
import { getRewardableData } from './utils/rewardable'
import HomeBridgeV1Abi from './utils/HomeBridgeV1.abi'
async function asyncForEach(array, callback) {
@ -42,24 +48,24 @@ async function asyncForEach(array, callback) {
}
class HomeStore {
@observable state = null;
@observable loading = true;
@observable events = [];
@observable errors = [];
@observable balance = "";
@observable filter = false;
@observable maxCurrentDeposit = "";
@observable maxPerTx = "";
@observable latestBlockNumber = 0;
@observable state = null
@observable loading = true
@observable events = []
@observable errors = []
@observable balance = ''
@observable filter = false
@observable maxCurrentDeposit = ''
@observable maxPerTx = ''
@observable latestBlockNumber = 0
@observable validators = []
@observable validatorsCount = 0
@observable homeBridgeValidators = ''
@observable requiredSignatures = 0
@observable dailyLimit = 0
@observable totalSpentPerDay = 0
@observable tokenAddress = '';
@observable symbol = process.env.REACT_APP_HOME_NATIVE_NAME || 'NONAME';
@observable tokenName = '';
@observable tokenAddress = ''
@observable symbol = process.env.REACT_APP_HOME_NATIVE_NAME || 'NONAME'
@observable tokenName = ''
@observable userBalance = 0
@observable statistics = {
deposits: 0,
@ -85,18 +91,18 @@ class HomeStore {
feeManager = {
totalFeeDistributedFromSignatures: BN(0),
totalFeeDistributedFromAffirmation: BN(0)
};
}
networkName = process.env.REACT_APP_HOME_NETWORK_NAME || 'Unknown'
filteredBlockNumber = 0
homeBridge = {};
HOME_BRIDGE_ADDRESS = process.env.REACT_APP_HOME_BRIDGE_ADDRESS;
homeBridge = {}
HOME_BRIDGE_ADDRESS = process.env.REACT_APP_HOME_BRIDGE_ADDRESS
explorerTxTemplate = process.env.REACT_APP_HOME_EXPLORER_TX_TEMPLATE || ''
explorerAddressTemplate = process.env.REACT_APP_HOME_EXPLORER_ADDRESS_TEMPLATE || ''
tokenContract = {}
tokenDecimals = 18;
tokenDecimals = 18
blockRewardContract = {}
constructor (rootStore) {
constructor(rootStore) {
this.homeWeb3 = rootStore.web3Store.homeWeb3
this.web3Store = rootStore.web3Store
this.alertStore = rootStore.alertStore
@ -105,16 +111,16 @@ class HomeStore {
this.setHome()
}
async setHome(){
async setHome() {
if (!this.rootStore.bridgeModeInitialized) {
setTimeout(() => this.setHome(), 200)
return
}
const { HOME_ABI } = getBridgeABIs(this.rootStore.bridgeMode)
this.homeBridge = new this.homeWeb3.eth.Contract(HOME_ABI, this.HOME_BRIDGE_ADDRESS);
this.homeBridge = new this.homeWeb3.eth.Contract(HOME_ABI, this.HOME_BRIDGE_ADDRESS)
if (this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_ERC) {
await this.getTokenInfo()
} else if(this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE) {
} else if (this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE) {
await this.getBlockRewardContract()
}
await this.getBlockNumber()
@ -139,22 +145,26 @@ class HomeStore {
async getTokenInfo() {
try {
this.tokenAddress = await getErc677TokenAddress(this.homeBridge)
this.tokenContract = new this.homeWeb3.eth.Contract(ERC677_ABI, this.tokenAddress);
this.tokenContract = new this.homeWeb3.eth.Contract(ERC677_ABI, this.tokenAddress)
this.symbol = await getSymbol(this.tokenContract)
this.tokenName = await getName(this.tokenContract)
const alternativeContract = new this.homeWeb3.eth.Contract(ERC20Bytes32Abi, this.tokenAddress);
const alternativeContract = new this.homeWeb3.eth.Contract(ERC20Bytes32Abi, this.tokenAddress)
try {
this.symbol =await getSymbol(this.tokenContract)
} catch(e) {
this.symbol = this.homeWeb3.utils.hexToAscii(await getSymbol(alternativeContract)).replace(/\u0000*$/, '')
this.symbol = await getSymbol(this.tokenContract)
} catch (e) {
this.symbol = this.homeWeb3.utils
.hexToAscii(await getSymbol(alternativeContract))
.replace(/\u0000*$/, '')
}
try {
this.tokenName = await getName(this.tokenContract)
} catch(e) {
this.tokenName = this.homeWeb3.utils.hexToAscii(await getName(alternativeContract)).replace(/\u0000*$/, '')
} catch (e) {
this.tokenName = this.homeWeb3.utils
.hexToAscii(await getName(alternativeContract))
.replace(/\u0000*$/, '')
}
this.tokenDecimals = await getDecimals(this.tokenContract)
} catch(e) {
} catch (e) {
console.error(e)
}
}
@ -163,25 +173,25 @@ class HomeStore {
async getBlockNumber() {
try {
this.latestBlockNumber = await getBlockNumber(this.homeWeb3)
} catch(e){
} catch (e) {
console.error(e)
}
}
@action
async getMaxPerTxLimit(){
async getMaxPerTxLimit() {
try {
this.maxPerTx = await getMaxPerTxLimit(this.homeBridge,this.tokenDecimals)
} catch(e){
this.maxPerTx = await getMaxPerTxLimit(this.homeBridge, this.tokenDecimals)
} catch (e) {
console.error(e)
}
}
@action
async getMinPerTxLimit(){
async getMinPerTxLimit() {
try {
this.minPerTx = await getMinPerTxLimit(this.homeBridge,this.tokenDecimals)
} catch(e){
this.minPerTx = await getMinPerTxLimit(this.homeBridge, this.tokenDecimals)
} catch (e) {
console.error(e)
}
}
@ -192,17 +202,20 @@ class HomeStore {
if (this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_ERC) {
this.balance = await getTotalSupply(this.tokenContract)
this.web3Store.getWeb3Promise.then(async () => {
this.userBalance = await getBalanceOf(this.tokenContract, this.web3Store.defaultAccount.address)
this.userBalance = await getBalanceOf(
this.tokenContract,
this.web3Store.defaultAccount.address
)
balanceLoaded()
})
} else if (this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE) {
const mintedCoins = await mintedTotally(this.blockRewardContract)
const burntCoins = await totalBurntCoins(this.homeBridge)
this.balance = fromDecimals(mintedCoins.minus(burntCoins).toString(10),this.tokenDecimals)
this.balance = fromDecimals(mintedCoins.minus(burntCoins).toString(10), this.tokenDecimals)
} else {
this.balance = await getBalance(this.homeWeb3, this.HOME_BRIDGE_ADDRESS)
}
} catch(e) {
} catch (e) {
console.error(e)
this.errors.push(e)
}
@ -215,17 +228,17 @@ class HomeStore {
const feeManagerModeHash = await getFeeManagerMode(this.homeBridge)
this.feeManager.feeManagerMode = decodeFeeManagerMode(feeManagerModeHash)
if(this.feeManager.feeManagerMode === FEE_MANAGER_MODE.BOTH_DIRECTIONS) {
if (this.feeManager.feeManagerMode === FEE_MANAGER_MODE.BOTH_DIRECTIONS) {
this.feeManager.homeFee = await getHomeFee(this.homeBridge)
this.feeManager.foreignFee = await getForeignFee(this.homeBridge)
} else {
this.feeManager.homeFee = new BN(0);
this.feeManager.homeFee = new BN(0)
this.feeManager.foreignFee = await getForeignFee(this.homeBridge)
}
} else {
this.feeManager.feeManagerMode = FEE_MANAGER_MODE.UNDEFINED
this.feeManager.homeFee = new BN(0);
this.feeManager.foreignFee = new BN(0);
this.feeManager.homeFee = new BN(0)
this.feeManager.foreignFee = new BN(0)
}
}
@ -233,60 +246,70 @@ class HomeStore {
async getEvents(fromBlock, toBlock) {
try {
fromBlock = fromBlock || this.filteredBlockNumber || this.latestBlockNumber - 50
toBlock = toBlock || this.filteredBlockNumber || "latest"
toBlock = toBlock || this.filteredBlockNumber || 'latest'
if (fromBlock < 0) {
fromBlock = 0
}
let events = await getPastEvents(this.homeBridge, fromBlock, toBlock).catch(e => {
console.error('Couldn\'t get events', e)
console.error("Couldn't get events", e)
return []
})
let homeEvents = []
await asyncForEach(events, (async (event) => {
if(event.event === "SignedForUserRequest" || event.event === "CollectedSignatures") {
await asyncForEach(events, async event => {
if (event.event === 'SignedForUserRequest' || event.event === 'CollectedSignatures') {
event.signedTxHash = await this.getSignedTx(event.returnValues.messageHash)
}
homeEvents.push(event)
}))
})
if(!this.filter){
this.events = homeEvents;
if (!this.filter) {
this.events = homeEvents
}
if(this.waitingForConfirmation.size) {
const confirmationEvents = homeEvents.filter((event) => event.event === "AffirmationCompleted" && this.waitingForConfirmation.has(event.returnValues.transactionHash))
if (this.waitingForConfirmation.size) {
const confirmationEvents = homeEvents.filter(
event =>
event.event === 'AffirmationCompleted' &&
this.waitingForConfirmation.has(event.returnValues.transactionHash)
)
confirmationEvents.forEach(event => {
this.alertStore.setLoadingStepIndex(3)
const urlExplorer = this.getExplorerTxUrl(event.transactionHash)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitHome
setTimeout(() => {
this.alertStore.pushSuccess(`${unitReceived} received on ${this.networkName} on Tx
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.networkName} on Tx
<a href='${urlExplorer}' target='blank' style="overflow-wrap: break-word;word-wrap: break-word;">
${event.transactionHash}</a>`, this.alertStore.HOME_TRANSFER_SUCCESS)}
, 2000)
${event.transactionHash}</a>`,
this.alertStore.HOME_TRANSFER_SUCCESS
)
}, 2000)
this.waitingForConfirmation.delete(event.returnValues.transactionHash)
})
if(confirmationEvents.length) {
if (confirmationEvents.length) {
removePendingTransaction()
}
}
return homeEvents
} catch(e) {
this.alertStore.pushError(`Cannot establish connection to Home Network.\n
Please make sure you have set it up in env variables`, this.alertStore.HOME_CONNECTION_ERROR)
} catch (e) {
this.alertStore.pushError(
`Cannot establish connection to Home Network.\n
Please make sure you have set it up in env variables`,
this.alertStore.HOME_CONNECTION_ERROR
)
}
}
async getSignedTx(messageHash){
async getSignedTx(messageHash) {
try {
const message = await getMessage(this.homeBridge, messageHash)
return "0x" + message.substring(106, 170);
} catch(e){
return '0x' + message.substring(106, 170)
} catch (e) {
console.error(e)
}
}
@ -301,37 +324,43 @@ class HomeStore {
@action
async filterByTxHashInReturnValues(transactionHash) {
const events = await this.getEvents(1,"latest");
this.events = events.filter((event) => event.returnValues.transactionHash === transactionHash)
const events = await this.getEvents(1, 'latest')
this.events = events.filter(event => event.returnValues.transactionHash === transactionHash)
}
@action
async filterByTxHash(transactionHash) {
const events = await this.getEvents(1,"latest");
this.events = events.filter((event) => event.transactionHash === transactionHash)
if(this.events.length > 0 && this.events[0].returnValues && this.events[0].returnValues.transactionHash) {
await this.rootStore.foreignStore.filterByTxHashInReturnValues(this.events[0].returnValues.transactionHash)
const events = await this.getEvents(1, 'latest')
this.events = events.filter(event => event.transactionHash === transactionHash)
if (
this.events.length > 0 &&
this.events[0].returnValues &&
this.events[0].returnValues.transactionHash
) {
await this.rootStore.foreignStore.filterByTxHashInReturnValues(
this.events[0].returnValues.transactionHash
)
}
}
@action
setFilter(value){
setFilter(value) {
this.filter = value
}
@action
async setBlockFilter(blockNumber){
async setBlockFilter(blockNumber) {
this.filteredBlockNumber = blockNumber
this.events = await this.getEvents()
}
@action
async getCurrentLimit(){
async getCurrentLimit() {
try {
const result = await getCurrentLimit(this.homeBridge,this.tokenDecimals)
const result = await getCurrentLimit(this.homeBridge, this.tokenDecimals)
this.maxCurrentDeposit = result.maxCurrentDeposit
this.dailyLimit = result.dailyLimit
this.totalSpentPerDay = result.totalSpentPerDay
} catch(e){
} catch (e) {
console.error(e)
}
}
@ -343,94 +372,114 @@ class HomeStore {
}
@action
async getValidators(){
async getValidators() {
try {
const homeValidatorsAddress = await this.homeBridge.methods.validatorContract().call()
this.homeBridgeValidators = new this.homeWeb3.eth.Contract(BRIDGE_VALIDATORS_ABI, homeValidatorsAddress);
this.homeBridgeValidators = new this.homeWeb3.eth.Contract(
BRIDGE_VALIDATORS_ABI,
homeValidatorsAddress
)
this.requiredSignatures = await this.homeBridgeValidators.methods.requiredSignatures().call()
this.validatorsCount = await this.homeBridgeValidators.methods.validatorCount().call()
this.validators = await getValidatorList(homeValidatorsAddress, this.homeWeb3.eth)
} catch(e){
} catch (e) {
console.error(e)
}
}
async getStatistics() {
try {
const deployedAtBlock = await getDeployedAtBlock(this.homeBridge);
const deployedAtBlock = await getDeployedAtBlock(this.homeBridge)
const { HOME_ABI } = getBridgeABIs(this.rootStore.bridgeMode)
const abi = [...HomeBridgeV1Abi, ...HOME_ABI]
const contract = new this.homeWeb3.eth.Contract(abi, this.HOME_BRIDGE_ADDRESS);
const contract = new this.homeWeb3.eth.Contract(abi, this.HOME_BRIDGE_ADDRESS)
const events = await getPastEvents(contract, deployedAtBlock, 'latest')
processLargeArrayAsync(
events,
this.processEvent,
() => {
this.statistics.finished = true
this.statistics.totalBridged = this.statistics.depositsValue.plus(this.statistics.withdrawsValue)
})
} catch(e){
processLargeArrayAsync(events, this.processEvent, () => {
this.statistics.finished = true
this.statistics.totalBridged = this.statistics.depositsValue.plus(
this.statistics.withdrawsValue
)
})
} catch (e) {
console.error(e)
this.getStatistics()
}
}
processEvent = (event) => {
if(event.returnValues && event.returnValues.recipient) {
processEvent = event => {
if (event.returnValues && event.returnValues.recipient) {
this.statistics.users.add(event.returnValues.recipient)
}
if(event.event === "UserRequestForSignature" || event.event === 'Deposit') {
if (event.event === 'UserRequestForSignature' || event.event === 'Deposit') {
this.statistics.deposits++
this.statistics.depositsValue = this.statistics.depositsValue.plus(BN(fromDecimals(event.returnValues.value,this.tokenDecimals)))
} else if (event.event === "AffirmationCompleted" || event.event === 'Withdraw') {
this.statistics.depositsValue = this.statistics.depositsValue.plus(
BN(fromDecimals(event.returnValues.value, this.tokenDecimals))
)
} else if (event.event === 'AffirmationCompleted' || event.event === 'Withdraw') {
this.statistics.withdraws++
this.statistics.withdrawsValue = this.statistics.withdrawsValue.plus(BN(fromDecimals(event.returnValues.value,this.tokenDecimals)))
} else if (event.event === "FeeDistributedFromSignatures") {
this.feeManager.totalFeeDistributedFromSignatures = this.feeManager.totalFeeDistributedFromSignatures.plus(BN(fromDecimals(event.returnValues.feeAmount, this.tokenDecimals)))
} else if (event.event === "FeeDistributedFromAffirmation") {
this.feeManager.totalFeeDistributedFromAffirmation = this.feeManager.totalFeeDistributedFromAffirmation.plus(BN(fromDecimals(event.returnValues.feeAmount, this.tokenDecimals)))
this.statistics.withdrawsValue = this.statistics.withdrawsValue.plus(
BN(fromDecimals(event.returnValues.value, this.tokenDecimals))
)
} else if (event.event === 'FeeDistributedFromSignatures') {
this.feeManager.totalFeeDistributedFromSignatures = this.feeManager.totalFeeDistributedFromSignatures.plus(
BN(fromDecimals(event.returnValues.feeAmount, this.tokenDecimals))
)
} else if (event.event === 'FeeDistributedFromAffirmation') {
this.feeManager.totalFeeDistributedFromAffirmation = this.feeManager.totalFeeDistributedFromAffirmation.plus(
BN(fromDecimals(event.returnValues.feeAmount, this.tokenDecimals))
)
}
}
calculateCollectedFees() {
if (!this.statistics.finished
|| !this.rootStore.foreignStore.feeEventsFinished
|| !this.feeManager.feeManagerMode
|| !this.rootStore.foreignStore.feeManager.feeManagerMode) {
setTimeout(() => { this.calculateCollectedFees() }, 1000)
if (
!this.statistics.finished ||
!this.rootStore.foreignStore.feeEventsFinished ||
!this.feeManager.feeManagerMode ||
!this.rootStore.foreignStore.feeManager.feeManagerMode
) {
setTimeout(() => {
this.calculateCollectedFees()
}, 1000)
return
}
const data = getRewardableData(this.feeManager, this.rootStore.foreignStore.feeManager)
this.depositFeeCollected.type = data.depositSymbol === 'home' ? this.symbol : this.rootStore.foreignStore.symbol
this.withdrawFeeCollected.type = data.withdrawSymbol === 'home' ? this.symbol : this.rootStore.foreignStore.symbol
this.depositFeeCollected.type =
data.depositSymbol === 'home' ? this.symbol : this.rootStore.foreignStore.symbol
this.withdrawFeeCollected.type =
data.withdrawSymbol === 'home' ? this.symbol : this.rootStore.foreignStore.symbol
this.depositFeeCollected.shouldDisplay = data.displayDeposit
this.withdrawFeeCollected.shouldDisplay = data.displayWithdraw
this.depositFeeCollected.value = data.depositSymbol === 'home'
? this.feeManager.totalFeeDistributedFromSignatures
: this.rootStore.foreignStore.feeManager.totalFeeDistributedFromSignatures
this.depositFeeCollected.value =
data.depositSymbol === 'home'
? this.feeManager.totalFeeDistributedFromSignatures
: this.rootStore.foreignStore.feeManager.totalFeeDistributedFromSignatures
this.withdrawFeeCollected.value = data.withdrawSymbol === 'home'
? this.feeManager.totalFeeDistributedFromAffirmation
: this.rootStore.foreignStore.feeManager.totalFeeDistributedFromAffirmation
this.withdrawFeeCollected.value =
data.withdrawSymbol === 'home'
? this.feeManager.totalFeeDistributedFromAffirmation
: this.rootStore.foreignStore.feeManager.totalFeeDistributedFromAffirmation
this.depositFeeCollected.finished = true
this.withdrawFeeCollected.finished = true
}
getDailyQuotaCompleted() {
return this.dailyLimit ? this.totalSpentPerDay / this.dailyLimit * 100 : 0
return this.dailyLimit ? (this.totalSpentPerDay / this.dailyLimit) * 100 : 0
}
getDisplayedBalance() {
return this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_ERC ? this.userBalance : this.web3Store.defaultAccount.homeBalance
return this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_ERC
? this.userBalance
: this.web3Store.defaultAccount.homeBalance
}
async getBlockRewardContract () {
async getBlockRewardContract() {
const blockRewardAddress = await this.homeBridge.methods.blockRewardContract().call()
this.blockRewardContract = new this.homeWeb3.eth.Contract(BLOCK_REWARD_ABI, blockRewardAddress)
}
@ -452,4 +501,4 @@ class HomeStore {
}
}
export default HomeStore;
export default HomeStore

View File

@ -1,4 +1,4 @@
import { action } from 'mobx';
import { action } from 'mobx'
import Web3Store from './Web3Store'
import HomeStore from './HomeStore'
import ForeignStore from './ForeignStore'
@ -24,11 +24,14 @@ class RootStore {
@action
async setBridgeMode() {
const homeWeb3 = getWeb3Instance(process.env.REACT_APP_HOME_HTTP_PARITY_URL)
const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_ABI, process.env.REACT_APP_HOME_BRIDGE_ADDRESS)
const homeBridge = new homeWeb3.eth.Contract(
HOME_ERC_ABI,
process.env.REACT_APP_HOME_BRIDGE_ADDRESS
)
const bridgeModeHash = await homeBridge.methods.getBridgeMode().call()
this.bridgeMode = decodeBridgeMode(bridgeModeHash)
this.bridgeModeInitialized = true
}
}
export default new RootStore();
export default new RootStore()

View File

@ -1,4 +1,4 @@
import { action, observable } from "mobx";
import { action, observable } from 'mobx'
import { estimateGas } from './utils/web3'
import { addPendingTransaction, removePendingTransaction } from './utils/testUtils'
import { getUnit } from './utils/bridgeMode'
@ -17,81 +17,91 @@ class TxStore {
}
@action
async doSend({to, from, value, data, sentValue}){
return this.web3Store.getWeb3Promise.then(async ()=> {
if(!this.web3Store.defaultAccount){
this.alertStore.pushError("Please unlock wallet")
async doSend({ to, from, value, data, sentValue }) {
return this.web3Store.getWeb3Promise.then(async () => {
if (!this.web3Store.defaultAccount) {
this.alertStore.pushError('Please unlock wallet')
return
}
try {
const gasPrice = this.gasPriceStore.gasPriceInHex
const gas = await estimateGas(this.web3Store.injectedWeb3, to, gasPrice, from, value, data)
return this.web3Store.injectedWeb3.eth.sendTransaction({
to,
gasPrice,
gas,
from,
value,
data,
chainId: this.web3Store.metamaskNet.id
}).on('transactionHash', (hash) => {
console.log('txHash', hash)
this.txsValues[hash] = sentValue
this.alertStore.setLoadingStepIndex(1)
addPendingTransaction()
this.getTxReceipt(hash)
}).on('error', (e) => {
if(!e.message.includes('not mined within 50 blocks') && !e.message.includes('Failed to subscribe to new newBlockHeaders')){
this.alertStore.setLoading(false)
this.alertStore.pushError('Transaction rejected on wallet');
}
})
} catch(e) {
this.alertStore.pushError(e.message);
return this.web3Store.injectedWeb3.eth
.sendTransaction({
to,
gasPrice,
gas,
from,
value,
data,
chainId: this.web3Store.metamaskNet.id
})
.on('transactionHash', hash => {
console.log('txHash', hash)
this.txsValues[hash] = sentValue
this.alertStore.setLoadingStepIndex(1)
addPendingTransaction()
this.getTxReceipt(hash)
})
.on('error', e => {
if (
!e.message.includes('not mined within 50 blocks') &&
!e.message.includes('Failed to subscribe to new newBlockHeaders')
) {
this.alertStore.setLoading(false)
this.alertStore.pushError('Transaction rejected on wallet')
}
})
} catch (e) {
this.alertStore.pushError(e.message)
}
})
}
@action
async erc677transferAndCall({to, from, value, contract, tokenAddress }){
async erc677transferAndCall({ to, from, value, contract, tokenAddress }) {
try {
return this.web3Store.getWeb3Promise.then(async () => {
if(this.web3Store.defaultAccount.address){
const data = await contract.methods.transferAndCall(
to, value, '0x00'
).encodeABI()
return this.doSend({to: tokenAddress, from, value: '0x00', data, sentValue: value})
if (this.web3Store.defaultAccount.address) {
const data = await contract.methods.transferAndCall(to, value, '0x00').encodeABI()
return this.doSend({ to: tokenAddress, from, value: '0x00', data, sentValue: value })
} else {
this.alertStore.pushError('Please unlock wallet');
this.alertStore.pushError('Please unlock wallet')
}
})
} catch(e) {
this.alertStore.pushError(e);
} catch (e) {
this.alertStore.pushError(e)
}
}
@action
async erc20transfer({to, from, value}){
async erc20transfer({ to, from, value }) {
try {
return this.web3Store.getWeb3Promise.then(async () => {
if(this.web3Store.defaultAccount.address){
const data = await this.foreignStore.tokenContract.methods.transfer(
to, value
).encodeABI({ from: this.web3Store.defaultAccount.address })
return this.doSend({to: this.foreignStore.tokenAddress, from, value: '0x', data, sentValue: value})
if (this.web3Store.defaultAccount.address) {
const data = await this.foreignStore.tokenContract.methods
.transfer(to, value)
.encodeABI({ from: this.web3Store.defaultAccount.address })
return this.doSend({
to: this.foreignStore.tokenAddress,
from,
value: '0x',
data,
sentValue: value
})
} else {
this.alertStore.pushError('Please unlock wallet');
this.alertStore.pushError('Please unlock wallet')
}
})
} catch(e) {
this.alertStore.pushError(e);
} catch (e) {
this.alertStore.pushError(e)
}
}
async getTxReceipt(hash){
const web3 = this.web3Store.injectedWeb3;
async getTxReceipt(hash) {
const web3 = this.web3Store.injectedWeb3
web3.eth.getTransaction(hash, (error, res) => {
if(res && res.blockNumber){
if (res && res.blockNumber) {
this.getTxStatus(hash)
} else {
console.log('not mined yet', hash)
@ -103,13 +113,13 @@ class TxStore {
}
async getTxStatus(hash) {
const web3 = this.web3Store.injectedWeb3;
const web3 = this.web3Store.injectedWeb3
web3.eth.getTransactionReceipt(hash, (error, res) => {
if(res && res.blockNumber){
if(this.isStatusSuccess(res)){
if(this.web3Store.metamaskNet.id === this.web3Store.homeNet.id) {
if (res && res.blockNumber) {
if (this.isStatusSuccess(res)) {
if (this.web3Store.metamaskNet.id === this.web3Store.homeNet.id) {
const blockConfirmations = this.homeStore.latestBlockNumber - res.blockNumber
if(blockConfirmations >= 8) {
if (blockConfirmations >= 8) {
this.alertStore.setBlockConfirmations(8)
this.alertStore.setLoadingStepIndex(2)
@ -118,27 +128,25 @@ class TxStore {
this.alertStore.setLoadingStepIndex(3)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitForeign
setTimeout(() => {
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.foreignStore.networkName}`,
this.alertStore.FOREIGN_TRANSFER_SUCCESS
)
}
, 2000)
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.foreignStore.networkName}`,
this.alertStore.FOREIGN_TRANSFER_SUCCESS
)
}, 2000)
removePendingTransaction()
})
} else {
this.foreignStore.addWaitingForConfirmation(hash)
}
} else {
if(blockConfirmations > 0) {
if (blockConfirmations > 0) {
this.alertStore.setBlockConfirmations(blockConfirmations)
}
this.getTxStatus(hash)
}
} else {
const blockConfirmations = this.foreignStore.latestBlockNumber - res.blockNumber
if(blockConfirmations >= 8) {
if (blockConfirmations >= 8) {
this.alertStore.setBlockConfirmations(8)
this.alertStore.setLoadingStepIndex(2)
@ -147,19 +155,18 @@ class TxStore {
this.alertStore.setLoadingStepIndex(3)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitHome
setTimeout(() => {
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.homeStore.networkName}`,
this.alertStore.HOME_TRANSFER_SUCCESS
)
}
, 2000)
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.homeStore.networkName}`,
this.alertStore.HOME_TRANSFER_SUCCESS
)
}, 2000)
removePendingTransaction()
})
} else {
this.homeStore.addWaitingForConfirmation(hash)
}
} else {
if(blockConfirmations > 0) {
if (blockConfirmations > 0) {
this.alertStore.setBlockConfirmations(blockConfirmations)
}
this.getTxStatus(hash)
@ -177,11 +184,10 @@ class TxStore {
isStatusSuccess(tx) {
const { toBN } = this.web3Store.injectedWeb3.utils
const statusSuccess = tx.status && (tx.status === true || toBN(tx.status).eq(toBN(1)))
const statusSuccess = tx.status && (tx.status === true || toBN(tx.status).eq(toBN(1)))
const eventEmitted = tx.logs && tx.logs.length
return statusSuccess || eventEmitted
}
}
export default TxStore;
export default TxStore

View File

@ -1,55 +1,57 @@
import { action, observable } from "mobx";
import getWeb3, { getBalance, getWeb3Instance, getNetwork } from './utils/web3';
import { action, observable } from 'mobx'
import getWeb3, { getBalance, getWeb3Instance, getNetwork } from './utils/web3'
import { balanceLoaded } from './utils/testUtils'
import { BRIDGE_MODES } from './utils/bridgeMode'
class Web3Store {
@observable injectedWeb3 = {};
@observable defaultAccount = {address: '', homeBalance: ''};
@observable injectedWeb3 = {}
@observable defaultAccount = { address: '', homeBalance: '' }
@observable homeWeb3 = {};
@observable foreignWeb3 = {};
@observable homeWeb3 = {}
@observable foreignWeb3 = {}
@observable loading = true;
@observable errors = [];
@observable loading = true
@observable errors = []
@observable getWeb3Promise = null;
@observable setHomeWeb3Promise = null;
@observable metamaskNotSetted = false;
@observable getWeb3Promise = null
@observable setHomeWeb3Promise = null
@observable metamaskNotSetted = false
@observable homeNet = {id: '', name: ''};
@observable foreignNet = {id: '', name: ''};
@observable metamaskNet = {id: '', name: ''};
@observable homeNet = { id: '', name: '' }
@observable foreignNet = { id: '', name: '' }
@observable metamaskNet = { id: '', name: '' }
@observable walletInstalled = true
HOME_HTTP_PARITY_URL = process.env.REACT_APP_HOME_HTTP_PARITY_URL;
FOREIGN_HTTP_PARITY_URL = process.env.REACT_APP_FOREIGN_HTTP_PARITY_URL;
HOME_HTTP_PARITY_URL = process.env.REACT_APP_HOME_HTTP_PARITY_URL
FOREIGN_HTTP_PARITY_URL = process.env.REACT_APP_FOREIGN_HTTP_PARITY_URL
constructor(rootStore) {
this.alertStore = rootStore.alertStore;
this.alertStore = rootStore.alertStore
this.rootStore = rootStore
this.getWeb3Promise = getWeb3()
this.getWeb3Promise.then((web3Config) => {
this.setWeb3State(web3Config)
this.getBalances(false)
setInterval(() => {
this.getBalances(true)
}, 3000)
}).catch(e => {
const error = {
rejected: () => this.alertStore.pushError(e.message),
unlock: () => this.alertStore.pushError(e.message),
install: () => this.walletInstalled = false
}[e.type]
this.getWeb3Promise
.then(web3Config => {
this.setWeb3State(web3Config)
this.getBalances(false)
setInterval(() => {
this.getBalances(true)
}, 3000)
})
.catch(e => {
const error = {
rejected: () => this.alertStore.pushError(e.message),
unlock: () => this.alertStore.pushError(e.message),
install: () => (this.walletInstalled = false)
}[e.type]
console.error(e.message, 'web3 not loaded')
this.errors.push(e.message)
this.metamaskNotSetted = true
error()
})
console.error(e.message, 'web3 not loaded')
this.errors.push(e.message)
this.metamaskNotSetted = true
error()
})
this.setWeb3Home()
this.setWeb3Foreign()
this.checkMetamaskConfig()
@ -57,11 +59,11 @@ class Web3Store {
@action
setWeb3State(web3Config) {
const {web3Instance, defaultAccount, netIdName, netId} = web3Config;
this.metamaskNet = {id: netId, name: netIdName};
this.defaultAccount.address = defaultAccount;
this.injectedWeb3 = web3Instance;
this.loading = false;
const { web3Instance, defaultAccount, netIdName, netId } = web3Config
this.metamaskNet = { id: netId, name: netIdName }
this.defaultAccount.address = defaultAccount
this.injectedWeb3 = web3Instance
this.loading = false
}
@action
@ -79,42 +81,54 @@ class Web3Store {
}
@action
async getBalances(displayLoading){
async getBalances(displayLoading) {
try {
const accounts = await this.injectedWeb3.eth.getAccounts()
const Loading = this.alertStore.showLoading
let accountUpdated = false
if(accounts[0] !== this.defaultAccount.address) {
if(displayLoading && !Loading && accounts[0] !== undefined) {
if (accounts[0] !== this.defaultAccount.address) {
if (displayLoading && !Loading && accounts[0] !== undefined) {
this.alertStore.setLoading(true)
accountUpdated = true
}
this.defaultAccount.address = accounts[0]
}
this.defaultAccount.homeBalance = await getBalance(this.homeWeb3, this.defaultAccount.address)
if(accountUpdated) {
if (accountUpdated) {
await this.rootStore.foreignStore.getTokenBalance()
await this.rootStore.homeStore.getBalance()
this.alertStore.setLoading(false)
}
if (this.rootStore.bridgeModeInitialized && this.rootStore.bridgeMode !== BRIDGE_MODES.ERC_TO_ERC) {
if (
this.rootStore.bridgeModeInitialized &&
this.rootStore.bridgeMode !== BRIDGE_MODES.ERC_TO_ERC
) {
balanceLoaded()
}
} catch(e){
} catch (e) {
console.error(e)
}
}
@action
checkMetamaskConfig() {
if(!this.metamaskNotSetted) {
if(this.metamaskNet.name === '' || this.homeNet.name === '' || this.foreignNet.name === '') {
setTimeout(() => {this.checkMetamaskConfig()}, 1000)
if (!this.metamaskNotSetted) {
if (this.metamaskNet.name === '' || this.homeNet.name === '' || this.foreignNet.name === '') {
setTimeout(() => {
this.checkMetamaskConfig()
}, 1000)
return
}
if(this.metamaskNet.name !== this.homeNet.name && this.metamaskNet.name !== this.foreignNet.name) {
if (
this.metamaskNet.name !== this.homeNet.name &&
this.metamaskNet.name !== this.foreignNet.name
) {
this.metamaskNotSetted = true
this.alertStore.pushError(`You are on an unknown network on your wallet. Please select ${this.homeNet.name} or ${this.foreignNet.name} in order to communicate with the bridge.`)
this.alertStore.pushError(
`You are on an unknown network on your wallet. Please select ${this.homeNet.name} or ${
this.foreignNet.name
} in order to communicate with the bridge.`
)
}
}
}
@ -126,4 +140,4 @@ class Web3Store {
}
}
export default Web3Store;
export default Web3Store

View File

@ -1,7 +1,7 @@
import TxStore from '../TxStore'
import Web3 from 'web3'
describe('TxStore', function () {
describe('TxStore', function() {
let txStore
beforeEach(() => {
const rootStore = {
@ -11,7 +11,7 @@ describe('TxStore', function () {
}
txStore = new TxStore(rootStore)
})
describe('isStatusSuccess', function () {
describe('isStatusSuccess', function() {
it('should return true if status field is 0x1', () => {
// Given
const tx = {
@ -60,9 +60,7 @@ describe('TxStore', function () {
it('should return true if status field not present and logs length > 0', () => {
// Given
const tx = {
logs: [
{}
]
logs: [{}]
}
// When

View File

@ -60,4 +60,3 @@ describe('getTokenType', () => {
expect(type).toEqual(ERC_TYPES.ERC20)
})
})

View File

@ -30,13 +30,13 @@ describe('getFeeToApply', () => {
const homeFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.BOTH_DIRECTIONS,
homeFee: new BN(0.01),
foreignFee: new BN(0.02),
foreignFee: new BN(0.02)
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.UNDEFINED,
homeFee: new BN(0),
foreignFee: new BN(0),
foreignFee: new BN(0)
}
const homeToForeignDirection = true
@ -53,13 +53,13 @@ describe('getFeeToApply', () => {
const homeFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.BOTH_DIRECTIONS,
homeFee: new BN(0.01),
foreignFee: new BN(0.02),
foreignFee: new BN(0.02)
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.UNDEFINED,
homeFee: new BN(0),
foreignFee: new BN(0),
foreignFee: new BN(0)
}
const homeToForeignDirection = false
@ -76,13 +76,13 @@ describe('getFeeToApply', () => {
const homeFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.ONE_DIRECTION,
homeFee: new BN(0),
foreignFee: new BN(0.02),
foreignFee: new BN(0.02)
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.ONE_DIRECTION,
homeFee: new BN(0.01),
foreignFee: new BN(0),
foreignFee: new BN(0)
}
const homeToForeignDirection = true
@ -99,13 +99,13 @@ describe('getFeeToApply', () => {
const homeFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.ONE_DIRECTION,
homeFee: new BN(0),
foreignFee: new BN(0.02),
foreignFee: new BN(0.02)
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.ONE_DIRECTION,
homeFee: new BN(0.01),
foreignFee: new BN(0),
foreignFee: new BN(0)
}
const homeToForeignDirection = false
@ -122,13 +122,13 @@ describe('getFeeToApply', () => {
const homeFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.UNDEFINED,
homeFee: new BN(0),
foreignFee: new BN(0),
foreignFee: new BN(0)
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.UNDEFINED,
homeFee: new BN(0),
foreignFee: new BN(0),
foreignFee: new BN(0)
}
const homeToForeignDirection = true
@ -144,13 +144,13 @@ describe('getFeeToApply', () => {
const homeFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.UNDEFINED,
homeFee: new BN(0),
foreignFee: new BN(0),
foreignFee: new BN(0)
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.UNDEFINED,
homeFee: new BN(0),
foreignFee: new BN(0),
foreignFee: new BN(0)
}
const homeToForeignDirection = false
@ -173,7 +173,7 @@ describe('getRewardableData', () => {
foreignHistoricFee: [{ blockNumber: 10, fee: new BN(0.02) }]
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.UNDEFINED,
homeFee: new BN(0),
foreignFee: new BN(0),
@ -181,7 +181,6 @@ describe('getRewardableData', () => {
foreignHistoricFee: []
}
// When
const result = getRewardableData(homeFeeManager, foreignFeeManager)
@ -205,7 +204,7 @@ describe('getRewardableData', () => {
foreignHistoricFee: [{ blockNumber: 10, fee: new BN(0.02) }]
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.ONE_DIRECTION,
homeFee: new BN(0.01),
foreignFee: new BN(0),
@ -213,7 +212,6 @@ describe('getRewardableData', () => {
foreignHistoricFee: []
}
// When
const result = getRewardableData(homeFeeManager, foreignFeeManager)
@ -237,7 +235,7 @@ describe('getRewardableData', () => {
foreignHistoricFee: [{ blockNumber: 10, fee: new BN(0.02) }]
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.UNDEFINED,
homeFee: new BN(0),
foreignFee: new BN(0),
@ -271,7 +269,7 @@ describe('getRewardableData', () => {
foreignHistoricFee: []
}
const foreignFeeManager = {
const foreignFeeManager = {
feeManagerMode: FEE_MANAGER_MODE.ONE_DIRECTION,
homeFee: new BN(0.01),
foreignFee: new BN(0),

View File

@ -5,10 +5,8 @@ describe('parseValidatorEvent', () => {
// Given
const event = {
raw: {
data: "0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225",
topics: [
"0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987"
],
data: '0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225',
topics: ['0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987']
},
returnValues: {}
}
@ -23,11 +21,11 @@ describe('parseValidatorEvent', () => {
// Given
const event = {
raw: {
data: "0x",
data: '0x',
topics: [
"0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987",
"0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225"
],
'0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987',
'0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225'
]
},
returnValues: {}
}
@ -42,11 +40,11 @@ describe('parseValidatorEvent', () => {
// Given
const event = {
raw: {
data: "0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225",
data: '0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225',
topics: [
"0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83",
"0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225"
],
'0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83',
'0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225'
]
},
returnValues: {}
}
@ -61,11 +59,11 @@ describe('parseValidatorEvent', () => {
// Given
const event = {
raw: {
data: "0x",
data: '0x',
topics: [
"0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1",
"0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225"
],
'0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1',
'0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225'
]
},
returnValues: {}
}
@ -80,10 +78,8 @@ describe('parseValidatorEvent', () => {
// Given
const event = {
raw: {
data: "0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225",
topics: [
"0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1"
],
data: '0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225',
topics: ['0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1']
},
returnValues: {}
}
@ -99,70 +95,71 @@ describe('processValidatorsEvents', () => {
it('should return validator list from raw events', () => {
// Given
const events = [
{ // Add v1 event
{
// Add v1 event
raw: {
data: "0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225",
topics: [
"0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987"
],
data: '0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225',
topics: ['0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987']
},
returnValues: {}
},
{ // Add v1 event
{
// Add v1 event
raw: {
data: "0x000000000000000000000000Cbd25A2a5708051747a052dBB1b291865Fc0e474",
topics: [
"0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987"
],
data: '0x000000000000000000000000Cbd25A2a5708051747a052dBB1b291865Fc0e474',
topics: ['0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987']
},
returnValues: {}
},
{ // Remove v1 event
{
// Remove v1 event
raw: {
data: "0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225",
topics: [
"0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1"
],
data: '0x000000000000000000000000cfef0c6bb765321529ffe81507f6d099693cd225',
topics: ['0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1']
},
returnValues: {}
},
{ // Add event
{
// Add event
raw: {
data: "0x",
data: '0x',
topics: [
"0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987",
"0x000000000000000000000000FE365A92Bc01425441dE76D8EDA48496B64446FB"
],
'0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987',
'0x000000000000000000000000FE365A92Bc01425441dE76D8EDA48496B64446FB'
]
},
returnValues: {}
},
{ // Add event
{
// Add event
raw: {
data: "0x",
data: '0x',
topics: [
"0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987",
"0x000000000000000000000000Bac68A86Cf596E3b124781E0bdbC47bb458bec62"
],
'0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987',
'0x000000000000000000000000Bac68A86Cf596E3b124781E0bdbC47bb458bec62'
]
},
returnValues: {}
},
{ // Remove event
{
// Remove event
raw: {
data: "0x",
data: '0x',
topics: [
"0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1",
"0x000000000000000000000000FE365A92Bc01425441dE76D8EDA48496B64446FB"
],
'0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1',
'0x000000000000000000000000FE365A92Bc01425441dE76D8EDA48496B64446FB'
]
},
returnValues: {}
},
{ // Add rewardable event
{
// Add rewardable event
raw: {
data: "0x0000000000000000000000000066938BBE9b31D44DFa8e27A1d675A545DF6ed5",
data: '0x0000000000000000000000000066938BBE9b31D44DFa8e27A1d675A545DF6ed5',
topics: [
"0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83",
"0x000000000000000000000000f4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955"
],
'0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83',
'0x000000000000000000000000f4BEF13F9f4f2B203FAF0C3cBbaAbe1afE056955'
]
},
returnValues: {}
}

View File

@ -1,22 +1,22 @@
export const processLargeArrayAsync = (array, fn, endFn, maxTimePerChunk = 16) => {
let index = 0;
let index = 0
const doChunk = () => {
const startTime = now();
while (index < array.length && (now() - startTime) <= maxTimePerChunk) {
fn(array[index]);
++index;
const startTime = now()
while (index < array.length && now() - startTime <= maxTimePerChunk) {
fn(array[index])
++index
}
if (index < array.length) {
setTimeout(doChunk, 0);
} else if(endFn) {
setTimeout(doChunk, 0)
} else if (endFn) {
endFn()
}
}
doChunk();
doChunk()
}
function now() {
return new Date().getTime();
return new Date().getTime()
}

View File

@ -22,7 +22,7 @@ export const ERC_TYPES = {
ERC20: 'ERC20'
}
export const getBridgeABIs = (bridgeMode) => {
export const getBridgeABIs = bridgeMode => {
let HOME_ABI = null
let FOREIGN_ABI = null
if (bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC) {
@ -41,7 +41,7 @@ export const getBridgeABIs = (bridgeMode) => {
return { HOME_ABI, FOREIGN_ABI }
}
export const decodeBridgeMode = (bridgeModeHash) => {
export const decodeBridgeMode = bridgeModeHash => {
switch (bridgeModeHash) {
case '0x92a8d7fe':
return BRIDGE_MODES.NATIVE_TO_ERC
@ -54,7 +54,7 @@ export const decodeBridgeMode = (bridgeModeHash) => {
}
}
export const getUnit = (bridgeMode) => {
export const getUnit = bridgeMode => {
let unitHome = null
let unitForeign = null
if (bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC) {
@ -73,7 +73,7 @@ export const getUnit = (bridgeMode) => {
return { unitHome, unitForeign }
}
export const decodeFeeManagerMode = (managerModeHash) => {
export const decodeFeeManagerMode = managerModeHash => {
switch (managerModeHash) {
case '0xf2aed8f7':
return FEE_MANAGER_MODE.ONE_DIRECTION

View File

@ -2,62 +2,63 @@ import BN from 'bignumber.js'
import { fromDecimals } from './decimals'
import { fromWei } from 'web3-utils'
import { abi as rewardableValidatorsAbi } from '../../contracts/RewardableValidators'
import { ERC_TYPES } from "./bridgeMode"
import { ERC_TYPES } from './bridgeMode'
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
export const getMaxPerTxLimit = async (contract,decimals) => {
export const getMaxPerTxLimit = async (contract, decimals) => {
const maxPerTx = await contract.methods.maxPerTx().call()
return fromDecimals(maxPerTx,decimals)
return fromDecimals(maxPerTx, decimals)
}
export const getMinPerTxLimit = async (contract,decimals) => {
export const getMinPerTxLimit = async (contract, decimals) => {
const minPerTx = await contract.methods.minPerTx().call()
return fromDecimals(minPerTx,decimals)
return fromDecimals(minPerTx, decimals)
}
export const getCurrentLimit = async (contract,decimals) => {
export const getCurrentLimit = async (contract, decimals) => {
const currentDay = await contract.methods.getCurrentDay().call()
const dailyLimit = await contract.methods.dailyLimit().call()
const totalSpentPerDay = await contract.methods.totalSpentPerDay(currentDay).call()
const maxCurrentDeposit = new BN(dailyLimit).minus(new BN(totalSpentPerDay)).toString(10)
return {
maxCurrentDeposit: fromDecimals(maxCurrentDeposit,decimals),
dailyLimit: fromDecimals(dailyLimit,decimals),
totalSpentPerDay: fromDecimals(totalSpentPerDay,decimals)
maxCurrentDeposit: fromDecimals(maxCurrentDeposit, decimals),
dailyLimit: fromDecimals(dailyLimit, decimals),
totalSpentPerDay: fromDecimals(totalSpentPerDay, decimals)
}
}
export const getPastEvents = (contract, fromBlock, toBlock, event = 'allEvents') => contract.getPastEvents(event, { fromBlock, toBlock })
export const getPastEvents = (contract, fromBlock, toBlock, event = 'allEvents') =>
contract.getPastEvents(event, { fromBlock, toBlock })
export const getErc677TokenAddress = (contract) => contract.methods.erc677token().call()
export const getErc677TokenAddress = contract => contract.methods.erc677token().call()
export const getErc20TokenAddress = (contract) => contract.methods.erc20token().call()
export const getErc20TokenAddress = contract => contract.methods.erc20token().call()
export const getSymbol = (contract) => contract.methods.symbol().call()
export const getSymbol = contract => contract.methods.symbol().call()
export const getDecimals= (contract) => contract.methods.decimals().call()
export const getDecimals = contract => contract.methods.decimals().call()
export const getMessage = (contract, messageHash) => contract.methods.message(messageHash).call()
export const getTotalSupply = async (contract) => {
export const getTotalSupply = async contract => {
const totalSupply = await contract.methods.totalSupply().call()
const decimals = await contract.methods.decimals().call()
return fromDecimals(totalSupply,decimals)
return fromDecimals(totalSupply, decimals)
}
export const getBalanceOf = async (contract, address) => {
const balance = await contract.methods.balanceOf(address).call()
const decimals = await contract.methods.decimals().call()
return fromDecimals(balance,decimals)
return fromDecimals(balance, decimals)
}
export const mintedTotally = async (contract) => {
export const mintedTotally = async contract => {
const mintedCoins = await contract.methods.mintedTotally().call()
return new BN(mintedCoins)
}
export const totalBurntCoins = async (contract) => {
export const totalBurntCoins = async contract => {
const burntCoins = await contract.methods.totalBurntCoins().call()
return new BN(burntCoins)
}
@ -66,18 +67,20 @@ export const getValidatorList = async (address, eth) => {
const validatorsContract = new eth.Contract(rewardableValidatorsAbi, address)
const validators = await validatorList(validatorsContract)
if(validators.length) {
if (validators.length) {
return validators
}
const deployedAtBlock = await getDeployedAtBlock(validatorsContract)
const contract = new eth.Contract([], address)
const validatorsEvents = await contract.getPastEvents('allEvents', { fromBlock: Number(deployedAtBlock) })
const validatorsEvents = await contract.getPastEvents('allEvents', {
fromBlock: Number(deployedAtBlock)
})
return processValidatorsEvents(validatorsEvents)
}
export const validatorList = async (contract) => {
export const validatorList = async contract => {
try {
return await contract.methods.validatorList().call()
} catch (e) {
@ -85,35 +88,39 @@ export const validatorList = async (contract) => {
}
}
export const processValidatorsEvents = (events) => {
export const processValidatorsEvents = events => {
const validatorList = new Set()
events.forEach(event => {
parseValidatorEvent(event)
if(event.event === 'ValidatorAdded') {
if (event.event === 'ValidatorAdded') {
validatorList.add(event.returnValues.validator)
} else if(event.event === 'ValidatorRemoved') {
} else if (event.event === 'ValidatorRemoved') {
validatorList.delete(event.returnValues.validator)
}
})
})
return Array.from(validatorList)
}
export const parseValidatorEvent = (event) => {
if (event.event === undefined
&& event.raw
&& event.raw.topics
&& (event.raw.topics[0] === '0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987'
|| event.raw.topics[0] === '0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83')) {
export const parseValidatorEvent = event => {
if (
event.event === undefined &&
event.raw &&
event.raw.topics &&
(event.raw.topics[0] === '0xe366c1c0452ed8eec96861e9e54141ebff23c9ec89fe27b996b45f5ec3884987' ||
event.raw.topics[0] === '0x8064a302796c89446a96d63470b5b036212da26bd2debe5bec73e0170a9a5e83')
) {
const rawAddress = event.raw.topics.length > 1 ? event.raw.topics[1] : event.raw.data
const address = '0x' + rawAddress.slice(26)
event.event = 'ValidatorAdded'
event.returnValues.validator = address
} else if (event.event === undefined
&& event.raw
&& event.raw.topics
&& event.raw.topics[0] === "0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1") {
} else if (
event.event === undefined &&
event.raw &&
event.raw.topics &&
event.raw.topics[0] === '0xe1434e25d6611e0db941968fdc97811c982ac1602e951637d206f5fdda9dd8f1'
) {
const rawAddress = event.raw.data === '0x' ? event.raw.topics[1] : event.raw.data
const address = '0x' + rawAddress.slice(26)
event.event = 'ValidatorRemoved'
@ -121,29 +128,29 @@ export const parseValidatorEvent = (event) => {
}
}
export const getName = (contract) => contract.methods.name().call()
export const getName = contract => contract.methods.name().call()
export const getFeeManager = async (contract) => {
try {
return await contract.methods.feeManagerContract().call()
} catch (e) {
return ZERO_ADDRESS
}
export const getFeeManager = async contract => {
try {
return await contract.methods.feeManagerContract().call()
} catch (e) {
return ZERO_ADDRESS
}
}
export const getFeeManagerMode = (contract) => contract.methods.getFeeManagerMode().call()
export const getFeeManagerMode = contract => contract.methods.getFeeManagerMode().call()
export const getHomeFee = async (contract) => {
export const getHomeFee = async contract => {
const feeInWei = await contract.methods.getHomeFee().call()
return new BN(fromWei(feeInWei.toString()))
}
export const getForeignFee = async (contract) => {
export const getForeignFee = async contract => {
const feeInWei = await contract.methods.getForeignFee().call()
return new BN(fromWei(feeInWei.toString()))
}
export const getDeployedAtBlock = async (contract) => {
export const getDeployedAtBlock = async contract => {
try {
return await contract.methods.deployedAtBlock().call()
} catch (e) {

View File

@ -1,36 +1,40 @@
import BN from 'bignumber.js'
export const fromDecimals = (number, decimals) => {
if (decimals == null) {
decimals = 18;
decimals = 18
}
const returnValue = toBigNumber(number).dividedBy(Math.pow(10, decimals));
return isBigNumber(number) ? returnValue : returnValue.toString(10);
};
const returnValue = toBigNumber(number).dividedBy(Math.pow(10, decimals))
return isBigNumber(number) ? returnValue : returnValue.toString(10)
}
export const toDecimals = (number, decimals) => {
if (decimals == null) {
decimals = 18;
decimals = 18
}
const returnValue = toBigNumber(number).times(Math.pow(10, decimals));
return isBigNumber(number) ? returnValue : returnValue.toString(10);
};
const returnValue = toBigNumber(number).times(Math.pow(10, decimals))
return isBigNumber(number) ? returnValue : returnValue.toString(10)
}
const isBigNumber = (object) => {
return (object && (object instanceof BN || (object.constructor && object.constructor.name === 'BigNumber')));
};
const isBigNumber = object => {
return (
object &&
(object instanceof BN || (object.constructor && object.constructor.name === 'BigNumber'))
)
}
const toBigNumber = (number) => {
/*jshint maxcomplexity:5 */
number = number || 0;
if (isBigNumber(number))
return number;
if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) {
return new BN(number.replace('0x',''), 16);
}
return new BN(number.toString(10), 10);
};
const toBigNumber = number => {
/*jshint maxcomplexity:5 */
number = number || 0
if (isBigNumber(number)) return number
if (isString(number) && (number.indexOf('0x') === 0 || number.indexOf('-0x') === 0)) {
return new BN(number.replace('0x', ''), 16)
}
return new BN(number.toString(10), 10)
}
const isString = (object) => {
return typeof object === 'string' ||
(object && object.constructor && object.constructor.name === 'String');
};
const isString = object => {
return (
typeof object === 'string' ||
(object && object.constructor && object.constructor.name === 'String')
)
}

View File

@ -13,7 +13,7 @@ export async function fetchGasPrice({ oracleFn }) {
}
export async function fetchGasPriceFromOracle(oracleUrl, speedType) {
if(!oracleUrl) {
if (!oracleUrl) {
throw new Error(`Gas Price Oracle url not defined`)
}
const response = await fetch(oracleUrl)

View File

@ -1,7 +1,7 @@
import BN from 'bignumber.js';
import BN from 'bignumber.js'
import { FEE_MANAGER_MODE } from './bridgeMode'
export const validFee = (fee) => {
export const validFee = fee => {
const zeroBN = new BN(0)
return !zeroBN.eq(fee)
}
@ -12,7 +12,7 @@ export const getFeeToApply = (homeFeeManager, foreignFeeManager, homeToForeignDi
}
export const getRewardableData = (homeFeeManager, foreignFeeManager) => {
if(homeFeeManager.feeManagerMode === FEE_MANAGER_MODE.BOTH_DIRECTIONS) {
if (homeFeeManager.feeManagerMode === FEE_MANAGER_MODE.BOTH_DIRECTIONS) {
return {
homeFee: homeFeeManager.homeFee,
foreignFee: homeFeeManager.foreignFee,
@ -23,8 +23,10 @@ export const getRewardableData = (homeFeeManager, foreignFeeManager) => {
displayDeposit: true,
displayWithdraw: true
}
} else if(homeFeeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION
&& foreignFeeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION) {
} else if (
homeFeeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION &&
foreignFeeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION
) {
return {
homeFee: foreignFeeManager.homeFee,
foreignFee: homeFeeManager.foreignFee,
@ -35,8 +37,10 @@ export const getRewardableData = (homeFeeManager, foreignFeeManager) => {
displayDeposit: true,
displayWithdraw: true
}
} else if(homeFeeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION
&& foreignFeeManager.feeManagerMode === FEE_MANAGER_MODE.UNDEFINED) {
} else if (
homeFeeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION &&
foreignFeeManager.feeManagerMode === FEE_MANAGER_MODE.UNDEFINED
) {
return {
homeFee: new BN(0),
foreignFee: homeFeeManager.foreignFee,
@ -47,8 +51,10 @@ export const getRewardableData = (homeFeeManager, foreignFeeManager) => {
displayDeposit: false,
displayWithdraw: true
}
} else if(homeFeeManager.feeManagerMode === FEE_MANAGER_MODE.UNDEFINED
&& foreignFeeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION) {
} else if (
homeFeeManager.feeManagerMode === FEE_MANAGER_MODE.UNDEFINED &&
foreignFeeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION
) {
return {
homeFee: foreignFeeManager.homeFee,
foreignFee: new BN(0),

View File

@ -3,7 +3,7 @@ let pendingTransaction = 0
export const balanceLoaded = () => {
balanceCount++
if(balanceCount > 1) {
if (balanceCount > 1) {
document.getElementById('root').classList.add('web3-loaded')
}
}
@ -15,7 +15,7 @@ export const addPendingTransaction = () => {
export const removePendingTransaction = () => {
pendingTransaction--
if(!pendingTransaction) {
if (!pendingTransaction) {
document.getElementById('root').classList.remove('pending-transaction')
}
}

View File

@ -5,24 +5,22 @@ const updateTitle = (networkName = 'No chain specified') => {
const defaultTitle = 'TokenBridge UI app'
if (!process.env.REACT_APP_TITLE) {
document.title = defaultTitle
}
else {
} else {
const titleReplaceString = '%c'
let appTitle = process.env.REACT_APP_TITLE || defaultTitle
if (appTitle.indexOf(titleReplaceString) !== -1) {
document.title = appTitle.replace(titleReplaceString, networkName)
}
else {
} else {
document.title = appTitle
}
}
}
const getWeb3 = () => {
return new Promise(function (resolve, reject) {
return new Promise(function(resolve, reject) {
// Wait for loading completion to avoid race conditions with web3 injection timing.
window.addEventListener('load',async function () {
window.addEventListener('load', async function() {
let web3 = window.web3
const { ethereum } = window
@ -33,7 +31,7 @@ const getWeb3 = () => {
try {
// Request account access
await ethereum.enable()
await processWeb3(web3, resolve, reject)
await processWeb3(web3, resolve, reject)
} catch (error) {
console.log(error)
const errorMsg = `Wallet account rejected by user. You need to unlock your wallet.
@ -42,13 +40,13 @@ const getWeb3 = () => {
}
} else if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider)
await processWeb3(web3, resolve, reject)
await processWeb3(web3, resolve, reject)
} else {
// Fallback to localhost if no web3 injection.
const errorMsg = ''
reject({ type: 'install', message: errorMsg })
console.log('No web3 instance injected, using Local web3.');
console.error('wallet not found');
console.log('No web3 instance injected, using Local web3.')
console.error('wallet not found')
}
})
})
@ -62,26 +60,26 @@ const networks = {
4: 'Rinkeby',
30: 'RSK Mainnet',
31: 'RSK Testnet',
42:'Kovan',
42: 'Kovan',
61: 'Ethereum Classic',
77:'Sokol',
77: 'Sokol',
99: 'POA Network',
100: 'Dai Chain'
}
export const getNetworkName = (id) => networks[id] || 'Unknown'
export const getNetworkName = id => networks[id] || 'Unknown'
export const getBalance = async (web3, address) => {
const balance = await web3.eth.getBalance(address)
return fromWei(balance)
}
export const getWeb3Instance = (provider) => {
const web3Provider = new Web3.providers.HttpProvider(provider);
return new Web3(web3Provider);
export const getWeb3Instance = provider => {
const web3Provider = new Web3.providers.HttpProvider(provider)
return new Web3(web3Provider)
}
export const getNetwork = async (web3) => {
export const getNetwork = async web3 => {
const id = await web3.eth.getChainId()
const name = getNetworkName(id)
return {
@ -90,28 +88,32 @@ export const getNetwork = async (web3) => {
}
}
export const getBlockNumber = (web3) => web3.eth.getBlockNumber()
export const getBlockNumber = web3 => web3.eth.getBlockNumber()
export const estimateGas = async (web3, to, gasPrice, from, value, data) =>{
const gas = await web3.eth.estimateGas({to, gasPrice, from, value, data})
export const estimateGas = async (web3, to, gasPrice, from, value, data) => {
const gas = await web3.eth.estimateGas({ to, gasPrice, from, value, data })
return toHex(gas.toString())
}
const processWeb3 = async (web3, resolve, reject) => {
const processWeb3 = async (web3, resolve, reject) => {
let netId
try {
netId = await web3.eth.getChainId()
} catch (error) {
reject({ type: 'unlock', message: 'Wallet does not support getting the Chain ID. Please use another wallet or specify a RPC url of a node that supports eth_chainId call' })
reject({
type: 'unlock',
message:
'Wallet does not support getting the Chain ID. Please use another wallet or specify a RPC url of a node that supports eth_chainId call'
})
}
const netIdName = getNetworkName(netId)
console.log(`This is ${netIdName} network.`, netId)
const accounts = await web3.eth.getAccounts()
const defaultAccount = accounts[0] || null;
const defaultAccount = accounts[0] || null
if(defaultAccount === null){
if (defaultAccount === null) {
reject({ type: 'unlock', message: 'Please unlock your wallet and refresh the page' })
}