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

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

22
.eslintrc Normal 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"
}
}

@ -1,21 +1,11 @@
{ {
"plugins": ["node", "prettier"], "plugins": ["node"],
"extends": [ "extends": [
"plugin:node/recommended", "plugin:node/recommended",
"airbnb-base", "airbnb-base",
"plugin:prettier/recommended" "../.eslintrc"
], ],
"rules": { "rules": {
"prettier/prettier": "error", "no-use-before-define": "off"
"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"
} }
} }

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

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

@ -2,20 +2,8 @@
"extends": [ "extends": [
"plugin:node/recommended", "plugin:node/recommended",
"airbnb-base", "airbnb-base",
"plugin:prettier/recommended" "../.eslintrc"
], ],
"plugins": ["node"], "plugins": ["node"],
"rules": { "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"
}
} }

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

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

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

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

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

@ -32,7 +32,7 @@
"web3-utils": "1.0.0-beta.30" "web3-utils": "1.0.0-beta.30"
}, },
"scripts": { "scripts": {
"lint": "eslint .", "lint": "eslint . --ignore-path ../.eslintignore",
"select-css-theme": "node scripts/selectTheme.js", "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", "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\"", "watch-css": "nodemon -e scss -x \"npm run build-css\"",

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,11 +1,26 @@
import React from 'react' 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>
<div className="events-header"> <div className="events-header">
<span className="events-header-title">Events</span> <span className="events-header-title">Events</span>
<div className="events-filter-container"> <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" /> <span className="events-filter-icon" />
<select value={selected} onChange={onChangeList} className="events-select"> <select value={selected} onChange={onChangeList} className="events-select">
<option value={homeValue}>{homeName}</option> <option value={homeValue}>{homeName}</option>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -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">
<div className="events-i-header-title"> <div className="events-i-header-title">
<p className={`label ${color}`}>{eventName}</p> <p className={`label ${color}`}>{eventName}</p>
</div> </div>
<p className="description break-all"> <p className="description break-all">tx: {transactionHash}</p>
tx: {transactionHash}
</p>
<div onClick={handleClick} className="events-i-switcher" /> <div onClick={handleClick} className="events-i-switcher" />
</div> </div>
) )

@ -1,4 +1,4 @@
import React from "react"; import React from 'react'
export const ArrowRight = () => ( export const ArrowRight = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="10"> <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" 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> </svg>
); )

@ -1,7 +1,10 @@
import React from "react" import React from 'react'
export const CopyIcon = () => ( export const CopyIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14"> <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> </svg>
) )

@ -1,7 +1,10 @@
import React from "react" import React from 'react'
export const InfoIcon = () => ( export const InfoIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"> <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> </svg>
) )

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

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

@ -1,7 +1,10 @@
import React from "react" import React from 'react'
export const MobileMenuCloseIcon = () => ( export const MobileMenuCloseIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"> <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> </svg>
) )

@ -1,7 +1,10 @@
import React from "react" import React from 'react'
export const MobileMenuIcon = () => ( export const MobileMenuIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="14"> <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> </svg>
) )

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

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

@ -1,7 +1,10 @@
import React from "react" import React from 'react'
export const WalletIcon = () => ( export const WalletIcon = () => (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18"> <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> </svg>
) )

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

@ -2,14 +2,11 @@ import React from 'react'
export const IconGithub = () => { export const IconGithub = () => {
return ( return (
<svg <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
viewBox="0 0 16 16" <path
xmlns="http://www.w3.org/2000/svg" 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"
<path />
fillRule="evenodd" </svg>
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>
) )
} }

@ -2,10 +2,7 @@ import React from 'react'
export const IconPOA = () => { export const IconPOA = () => {
return ( return (
<svg <svg viewBox="0 0 125 40" xmlns="http://www.w3.org/2000/svg">
viewBox="0 0 125 40"
xmlns="http://www.w3.org/2000/svg"
>
<g fillRule="evenodd"> <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" /> <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> </g>

@ -2,10 +2,7 @@ import React from 'react'
export const IconTelegram = () => { export const IconTelegram = () => {
return ( return (
<svg <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path <path
clipRule="evenodd" 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" 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"

@ -2,10 +2,7 @@ import React from 'react'
export const IconTwitter = () => { export const IconTwitter = () => {
return ( return (
<svg <svg viewBox="0 0 16 12" xmlns="http://www.w3.org/2000/svg">
viewBox="0 0 16 12"
xmlns="http://www.w3.org/2000/svg"
>
<path <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" 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" fillRule="evenodd"

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

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

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

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

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

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

@ -1,5 +1,5 @@
import { observable, computed } from "mobx"; import { observable, computed } from 'mobx'
import { toHex } from 'web3-utils'; import { toHex } from 'web3-utils'
import { fetchGasPrice, fetchGasPriceFromOracle } from './utils/gas' import { fetchGasPrice, fetchGasPriceFromOracle } from './utils/gas'
const HOME_GAS_PRICE_FALLBACK = process.env.REACT_APP_HOME_GAS_PRICE_FALLBACK 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

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

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

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

@ -1,55 +1,57 @@
import { action, observable } from "mobx"; import { action, observable } from 'mobx'
import getWeb3, { getBalance, getWeb3Instance, getNetwork } from './utils/web3'; import getWeb3, { getBalance, getWeb3Instance, getNetwork } from './utils/web3'
import { balanceLoaded } from './utils/testUtils' import { balanceLoaded } from './utils/testUtils'
import { BRIDGE_MODES } from './utils/bridgeMode' import { BRIDGE_MODES } from './utils/bridgeMode'
class Web3Store { class Web3Store {
@observable injectedWeb3 = {}; @observable injectedWeb3 = {}
@observable defaultAccount = {address: '', homeBalance: ''}; @observable defaultAccount = { address: '', homeBalance: '' }
@observable homeWeb3 = {}; @observable homeWeb3 = {}
@observable foreignWeb3 = {}; @observable foreignWeb3 = {}
@observable loading = true; @observable loading = true
@observable errors = []; @observable errors = []
@observable getWeb3Promise = null; @observable getWeb3Promise = null
@observable setHomeWeb3Promise = null; @observable setHomeWeb3Promise = null
@observable metamaskNotSetted = false; @observable metamaskNotSetted = false
@observable homeNet = {id: '', name: ''}; @observable homeNet = { id: '', name: '' }
@observable foreignNet = {id: '', name: ''}; @observable foreignNet = { id: '', name: '' }
@observable metamaskNet = {id: '', name: ''}; @observable metamaskNet = { id: '', name: '' }
@observable walletInstalled = true @observable walletInstalled = true
HOME_HTTP_PARITY_URL = process.env.REACT_APP_HOME_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; FOREIGN_HTTP_PARITY_URL = process.env.REACT_APP_FOREIGN_HTTP_PARITY_URL
constructor(rootStore) { constructor(rootStore) {
this.alertStore = rootStore.alertStore; this.alertStore = rootStore.alertStore
this.rootStore = rootStore this.rootStore = rootStore
this.getWeb3Promise = getWeb3() this.getWeb3Promise = getWeb3()
this.getWeb3Promise.then((web3Config) => { this.getWeb3Promise
this.setWeb3State(web3Config) .then(web3Config => {
this.getBalances(false) this.setWeb3State(web3Config)
setInterval(() => { this.getBalances(false)
this.getBalances(true) setInterval(() => {
}, 3000) this.getBalances(true)
}).catch(e => { }, 3000)
const error = { })
rejected: () => this.alertStore.pushError(e.message), .catch(e => {
unlock: () => this.alertStore.pushError(e.message), const error = {
install: () => this.walletInstalled = false rejected: () => this.alertStore.pushError(e.message),
}[e.type] unlock: () => this.alertStore.pushError(e.message),
install: () => (this.walletInstalled = false)
}[e.type]
console.error(e.message, 'web3 not loaded') console.error(e.message, 'web3 not loaded')
this.errors.push(e.message) this.errors.push(e.message)
this.metamaskNotSetted = true this.metamaskNotSetted = true
error() error()
}) })
this.setWeb3Home() this.setWeb3Home()
this.setWeb3Foreign() this.setWeb3Foreign()
this.checkMetamaskConfig() this.checkMetamaskConfig()
@ -57,11 +59,11 @@ class Web3Store {
@action @action
setWeb3State(web3Config) { setWeb3State(web3Config) {
const {web3Instance, defaultAccount, netIdName, netId} = web3Config; const { web3Instance, defaultAccount, netIdName, netId } = web3Config
this.metamaskNet = {id: netId, name: netIdName}; this.metamaskNet = { id: netId, name: netIdName }
this.defaultAccount.address = defaultAccount; this.defaultAccount.address = defaultAccount
this.injectedWeb3 = web3Instance; this.injectedWeb3 = web3Instance
this.loading = false; this.loading = false
} }
@action @action
@ -79,42 +81,54 @@ class Web3Store {
} }
@action @action
async getBalances(displayLoading){ async getBalances(displayLoading) {
try { try {
const accounts = await this.injectedWeb3.eth.getAccounts() const accounts = await this.injectedWeb3.eth.getAccounts()
const Loading = this.alertStore.showLoading const Loading = this.alertStore.showLoading
let accountUpdated = false let accountUpdated = false
if(accounts[0] !== this.defaultAccount.address) { if (accounts[0] !== this.defaultAccount.address) {
if(displayLoading && !Loading && accounts[0] !== undefined) { if (displayLoading && !Loading && accounts[0] !== undefined) {
this.alertStore.setLoading(true) this.alertStore.setLoading(true)
accountUpdated = true accountUpdated = true
} }
this.defaultAccount.address = accounts[0] this.defaultAccount.address = accounts[0]
} }
this.defaultAccount.homeBalance = await getBalance(this.homeWeb3, this.defaultAccount.address) this.defaultAccount.homeBalance = await getBalance(this.homeWeb3, this.defaultAccount.address)
if(accountUpdated) { if (accountUpdated) {
await this.rootStore.foreignStore.getTokenBalance() await this.rootStore.foreignStore.getTokenBalance()
await this.rootStore.homeStore.getBalance() await this.rootStore.homeStore.getBalance()
this.alertStore.setLoading(false) 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() balanceLoaded()
} }
} catch(e){ } catch (e) {
console.error(e) console.error(e)
} }
} }
@action @action
checkMetamaskConfig() { checkMetamaskConfig() {
if(!this.metamaskNotSetted) { if (!this.metamaskNotSetted) {
if(this.metamaskNet.name === '' || this.homeNet.name === '' || this.foreignNet.name === '') { if (this.metamaskNet.name === '' || this.homeNet.name === '' || this.foreignNet.name === '') {
setTimeout(() => {this.checkMetamaskConfig()}, 1000) setTimeout(() => {
this.checkMetamaskConfig()
}, 1000)
return 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.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

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

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

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

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

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

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

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

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

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

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

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

@ -5,24 +5,22 @@ const updateTitle = (networkName = 'No chain specified') => {
const defaultTitle = 'TokenBridge UI app' const defaultTitle = 'TokenBridge UI app'
if (!process.env.REACT_APP_TITLE) { if (!process.env.REACT_APP_TITLE) {
document.title = defaultTitle document.title = defaultTitle
} } else {
else {
const titleReplaceString = '%c' const titleReplaceString = '%c'
let appTitle = process.env.REACT_APP_TITLE || defaultTitle let appTitle = process.env.REACT_APP_TITLE || defaultTitle
if (appTitle.indexOf(titleReplaceString) !== -1) { if (appTitle.indexOf(titleReplaceString) !== -1) {
document.title = appTitle.replace(titleReplaceString, networkName) document.title = appTitle.replace(titleReplaceString, networkName)
} } else {
else {
document.title = appTitle document.title = appTitle
} }
} }
} }
const getWeb3 = () => { 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. // 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 let web3 = window.web3
const { ethereum } = window const { ethereum } = window
@ -33,7 +31,7 @@ const getWeb3 = () => {
try { try {
// Request account access // Request account access
await ethereum.enable() await ethereum.enable()
await processWeb3(web3, resolve, reject) await processWeb3(web3, resolve, reject)
} catch (error) { } catch (error) {
console.log(error) console.log(error)
const errorMsg = `Wallet account rejected by user. You need to unlock your wallet. const errorMsg = `Wallet account rejected by user. You need to unlock your wallet.
@ -42,13 +40,13 @@ const getWeb3 = () => {
} }
} else if (typeof web3 !== 'undefined') { } else if (typeof web3 !== 'undefined') {
web3 = new Web3(web3.currentProvider) web3 = new Web3(web3.currentProvider)
await processWeb3(web3, resolve, reject) await processWeb3(web3, resolve, reject)
} else { } else {
// Fallback to localhost if no web3 injection. // Fallback to localhost if no web3 injection.
const errorMsg = '' const errorMsg = ''
reject({ type: 'install', message: errorMsg }) reject({ type: 'install', message: errorMsg })
console.log('No web3 instance injected, using Local web3.'); console.log('No web3 instance injected, using Local web3.')
console.error('wallet not found'); console.error('wallet not found')
} }
}) })
}) })
@ -62,26 +60,26 @@ const networks = {
4: 'Rinkeby', 4: 'Rinkeby',
30: 'RSK Mainnet', 30: 'RSK Mainnet',
31: 'RSK Testnet', 31: 'RSK Testnet',
42:'Kovan', 42: 'Kovan',
61: 'Ethereum Classic', 61: 'Ethereum Classic',
77:'Sokol', 77: 'Sokol',
99: 'POA Network', 99: 'POA Network',
100: 'Dai Chain' 100: 'Dai Chain'
} }
export const getNetworkName = (id) => networks[id] || 'Unknown' export const getNetworkName = id => networks[id] || 'Unknown'
export const getBalance = async (web3, address) => { export const getBalance = async (web3, address) => {
const balance = await web3.eth.getBalance(address) const balance = await web3.eth.getBalance(address)
return fromWei(balance) return fromWei(balance)
} }
export const getWeb3Instance = (provider) => { export const getWeb3Instance = provider => {
const web3Provider = new Web3.providers.HttpProvider(provider); const web3Provider = new Web3.providers.HttpProvider(provider)
return new Web3(web3Provider); return new Web3(web3Provider)
} }
export const getNetwork = async (web3) => { export const getNetwork = async web3 => {
const id = await web3.eth.getChainId() const id = await web3.eth.getChainId()
const name = getNetworkName(id) const name = getNetworkName(id)
return { 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) =>{ export const estimateGas = async (web3, to, gasPrice, from, value, data) => {
const gas = await web3.eth.estimateGas({to, gasPrice, from, value, data}) const gas = await web3.eth.estimateGas({ to, gasPrice, from, value, data })
return toHex(gas.toString()) return toHex(gas.toString())
} }
const processWeb3 = async (web3, resolve, reject) => { const processWeb3 = async (web3, resolve, reject) => {
let netId let netId
try { try {
netId = await web3.eth.getChainId() netId = await web3.eth.getChainId()
} catch (error) { } 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) const netIdName = getNetworkName(netId)
console.log(`This is ${netIdName} network.`, netId) console.log(`This is ${netIdName} network.`, netId)
const accounts = await web3.eth.getAccounts() 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' }) reject({ type: 'unlock', message: 'Please unlock your wallet and refresh the page' })
} }