Add support for STAKE token ERC677-to-ERC677 through AMB mode in Bridge UI (#328)

This commit is contained in:
Gerardo Nardelli 2020-05-18 17:36:52 -03:00 committed by GitHub
parent 10f67168a7
commit 62f9a080c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 529 additions and 186 deletions

@ -7,13 +7,17 @@ const FOREIGN_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/ForeignB
const ERC20_ABI = require('../contracts/build/contracts/ERC20').abi
const ERC677_ABI = require('../contracts/build/contracts/ERC677').abi
const ERC677_BRIDGE_TOKEN_ABI = require('../contracts/build/contracts/ERC677BridgeToken').abi
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/IBlockReward').abi
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/BlockReward').abi
const BRIDGE_VALIDATORS_ABI = require('../contracts/build/contracts/BridgeValidators').abi
const REWARDABLE_VALIDATORS_ABI = require('../contracts/build/contracts/RewardableValidators').abi
const HOME_AMB_ABI = require('../contracts/build/contracts/HomeAMB').abi
const FOREIGN_AMB_ABI = require('../contracts/build/contracts/ForeignAMB').abi
const BOX_ABI = require('../contracts/build/contracts/Box').abi
const SAI_TOP = require('../contracts/build/contracts/SaiTopMock').abi
const HOME_AMB_ERC_TO_ERC_ABI = require('../contracts/build/contracts/HomeAMBErc677ToErc677').abi
const FOREIGN_AMB_ERC_TO_ERC_ABI = require('../contracts/build/contracts/ForeignAMBErc677ToErc677').abi
const HOME_STAKE_ERC_TO_ERC_ABI = require('../contracts/build/contracts/HomeStakeTokenMediator').abi
const FOREIGN_STAKE_ERC_TO_ERC_ABI = require('../contracts/build/contracts/ForeignStakeTokenMediator').abi
const { HOME_V1_ABI, FOREIGN_V1_ABI } = require('./v1Abis')
const { BRIDGE_MODES } = require('./constants')
@ -67,6 +71,12 @@ function getBridgeABIs(bridgeMode) {
} else if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
HOME_ABI = HOME_AMB_ABI
FOREIGN_ABI = FOREIGN_AMB_ABI
} else if (bridgeMode === BRIDGE_MODES.AMB_ERC_TO_ERC) {
HOME_ABI = HOME_AMB_ERC_TO_ERC_ABI
FOREIGN_ABI = FOREIGN_AMB_ERC_TO_ERC_ABI
} else if (bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC) {
HOME_ABI = HOME_STAKE_ERC_TO_ERC_ABI
FOREIGN_ABI = FOREIGN_STAKE_ERC_TO_ERC_ABI
} else {
throw new Error(`Unrecognized bridge mode: ${bridgeMode}`)
}
@ -94,5 +104,7 @@ module.exports = {
HOME_AMB_ABI,
FOREIGN_AMB_ABI,
BOX_ABI,
SAI_TOP
SAI_TOP,
HOME_STAKE_ERC_TO_ERC_ABI,
FOREIGN_STAKE_ERC_TO_ERC_ABI
}

@ -3,7 +3,9 @@ const BRIDGE_MODES = {
ERC_TO_ERC: 'ERC_TO_ERC',
ERC_TO_NATIVE: 'ERC_TO_NATIVE',
NATIVE_TO_ERC_V1: 'NATIVE_TO_ERC_V1',
ARBITRARY_MESSAGE: 'ARBITRARY_MESSAGE'
ARBITRARY_MESSAGE: 'ARBITRARY_MESSAGE',
AMB_ERC_TO_ERC: 'AMB_ERC_TO_ERC',
STAKE_AMB_ERC_TO_ERC: 'STAKE_AMB_ERC_TO_ERC'
}
const ERC_TYPES = {
@ -14,6 +16,7 @@ const ERC_TYPES = {
const FEE_MANAGER_MODE = {
ONE_DIRECTION: 'ONE_DIRECTION',
BOTH_DIRECTIONS: 'BOTH_DIRECTIONS',
ONE_DIRECTION_STAKE: 'ONE_DIRECTION_STAKE',
UNDEFINED: 'UNDEFINED'
}

@ -3,7 +3,7 @@ const { BRIDGE_MODES, ERC_TYPES } = require('../constants')
describe('constants', () => {
it('should contain correct number of bridge types', () => {
expect(Object.keys(BRIDGE_MODES).length).to.be.equal(5)
expect(Object.keys(BRIDGE_MODES).length).to.be.equal(7)
})
it('should contain correct number of erc types', () => {

@ -61,4 +61,99 @@ describe('getTokenType', () => {
// Then
expect(type).to.equal(ERC_TYPES.ERC20)
})
it('should return ERC20 if bridgeContract and isBridge are not present', async () => {
// Given
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
const contract = {
methods: {
bridgeContract: () => {
return {
call: () => Promise.reject()
}
},
isBridge: () => {
return {
call: () => Promise.reject()
}
}
}
}
// When
const type = await getTokenType(contract, bridgeAddress)
// Then
expect(type).to.equal(ERC_TYPES.ERC20)
})
it('should return ERC677 if isBridge returns true', async () => {
// Given
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
const contract = {
methods: {
bridgeContract: () => {
return {
call: () => Promise.reject()
}
},
isBridge: () => {
return {
call: () => Promise.resolve(true)
}
}
}
}
// When
const type = await getTokenType(contract, bridgeAddress)
// Then
expect(type).to.equal(ERC_TYPES.ERC677)
})
it('should return ERC677 if isBridge returns true and bridgeContract not present', async () => {
// Given
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
const contract = {
methods: {
isBridge: () => {
return {
call: () => Promise.resolve(true)
}
}
}
}
// When
const type = await getTokenType(contract, bridgeAddress)
// Then
expect(type).to.equal(ERC_TYPES.ERC677)
})
it('should return ERC20 if isBridge returns false', async () => {
// Given
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
const contract = {
methods: {
bridgeContract: () => {
return {
call: () => Promise.reject()
}
},
isBridge: () => {
return {
call: () => Promise.resolve(false)
}
}
}
}
// When
const type = await getTokenType(contract, bridgeAddress)
// Then
expect(type).to.equal(ERC_TYPES.ERC20)
})
})

@ -12,6 +12,10 @@ function decodeBridgeMode(bridgeModeHash) {
return BRIDGE_MODES.ERC_TO_NATIVE
case '0x2544fbb9':
return BRIDGE_MODES.ARBITRARY_MESSAGE
case '0x16ea01e9':
return BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC
case '0x76595b56':
return BRIDGE_MODES.AMB_ERC_TO_ERC
default:
throw new Error(`Unrecognized bridge mode hash: '${bridgeModeHash}'`)
}
@ -46,10 +50,31 @@ const getTokenType = async (bridgeTokenContract, bridgeAddress) => {
return ERC_TYPES.ERC20
}
} catch (e) {
return ERC_TYPES.ERC20
try {
const isBridge = await bridgeTokenContract.methods.isBridge(bridgeAddress).call()
if (isBridge) {
return ERC_TYPES.ERC677
} else {
return ERC_TYPES.ERC20
}
} catch (e) {
return ERC_TYPES.ERC20
}
}
}
const isErcToErcMode = bridgeMode => {
return (
bridgeMode === BRIDGE_MODES.ERC_TO_ERC ||
bridgeMode === BRIDGE_MODES.AMB_ERC_TO_ERC ||
bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC
)
}
const isMediatorMode = bridgeMode => {
return bridgeMode === BRIDGE_MODES.AMB_ERC_TO_ERC || bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC
}
const getUnit = bridgeMode => {
let unitHome = null
let unitForeign = null
@ -62,6 +87,9 @@ const getUnit = bridgeMode => {
} else if (bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE) {
unitHome = 'Native coins'
unitForeign = 'Tokens'
} else if (bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC) {
unitHome = 'Tokens'
unitForeign = 'Tokens'
} else {
throw new Error(`Unrecognized bridge mode: ${bridgeMode}`)
}
@ -260,5 +288,7 @@ module.exports = {
normalizeGasPrice,
gasPriceFromSupplier,
gasPriceFromContract,
gasPriceWithinLimits
gasPriceWithinLimits,
isErcToErcMode,
isMediatorMode
}

@ -1 +1 @@
Subproject commit a7ce4441ab77e1c3e4d01017d862c53516933645
Subproject commit e550067c7680d416f40de95fc4c8e5b073279ee6

@ -4,7 +4,7 @@ import { toHex } from 'web3-utils'
import foreignLogoPurple from '../assets/images/logos/logo-poa-20-purple@2x.png'
import homeLogoPurple from '../assets/images/logos/logo-poa-sokol-purple@2x.png'
import swal from 'sweetalert'
import { BRIDGE_MODES, ERC_TYPES } from '../../../commons'
import { BRIDGE_MODES, ERC_TYPES, isErcToErcMode } from '../../../commons'
import { BridgeAddress } from './index'
import { BridgeForm } from './index'
import { BridgeNetwork } from './index'
@ -63,7 +63,6 @@ export class Bridge extends React.Component {
async _sendToHome(amount) {
const { web3Store, homeStore, alertStore, txStore, bridgeMode } = this.props.RootStore
const isErcToErcMode = bridgeMode === BRIDGE_MODES.ERC_TO_ERC
const { isLessThan, isGreaterThan } = this
if (web3Store.metamaskNet.id.toString() !== web3Store.homeNet.id.toString()) {
swal('Error', `Please switch wallet to ${web3Store.homeNet.name} network`, 'error')
@ -98,7 +97,7 @@ export class Bridge extends React.Component {
} else {
try {
alertStore.setLoading(true)
if (isErcToErcMode) {
if (isErcToErcMode(bridgeMode)) {
return txStore.erc677transferAndCall({
to: homeStore.COMMON_HOME_BRIDGE_ADDRESS,
from: web3Store.defaultAccount.address,
@ -259,7 +258,6 @@ export class Bridge extends React.Component {
loadHomeDetails = () => {
const { web3Store, homeStore, bridgeMode } = this.props.RootStore
const isErcToErcMode = bridgeMode === BRIDGE_MODES.ERC_TO_ERC
const isExternalErc20 = bridgeMode === BRIDGE_MODES.ERC_TO_ERC || bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE
const modalData = {
@ -274,7 +272,7 @@ export class Bridge extends React.Component {
minPerTx: homeStore.minPerTx,
totalBalance: homeStore.balance,
balance: homeStore.getDisplayedBalance(),
displayTokenAddress: isErcToErcMode,
displayTokenAddress: isErcToErcMode(bridgeMode),
tokenAddress: homeStore.tokenAddress,
tokenName: homeStore.tokenName,
displayBridgeLimits: true,

@ -9,7 +9,8 @@ export const BridgeStatistics = ({
homeNativeSupplyTitle,
foreignSupply,
homeSymbol,
foreignSymbol
foreignSymbol,
displayNetworkTokenSupply
}) => (
<div className="statistics-bridge-data">
<DataBlock description="Users" value={numeral(users).format('0,0')} type="" />
@ -21,7 +22,13 @@ export const BridgeStatistics = ({
/>
<div className="separator" />
<DataBlock
description={homeNativeSupplyTitle ? `Native Coins Amount` : `Totally minted by the bridge`}
description={
displayNetworkTokenSupply
? `${homeSymbol} Tokens Amount`
: homeNativeSupplyTitle
? `Native Coins Amount`
: `Totally minted by the bridge`
}
value={numeral(homeBalance).format('0.00 a', Math.floor)}
type={homeSymbol}
/>

@ -10,7 +10,7 @@ export const FeeStatistics = ({ depositFeeCollected, withdrawFeeCollected }) =>
<div className="statistics-fee-data" data-testid="fee-statistics-data">
{depositFeeCollected.shouldDisplay && (
<DataBlock
description="Deposit Fees"
description={depositFeeCollected.title ? depositFeeCollected.title : 'Deposit Fees'}
value={numeral(depositFeeCollected.value).format('0,0.00 a', Math.floor)}
type={depositFeeCollected.type}
dataTestid="deposit-fees-block"
@ -19,7 +19,7 @@ export const FeeStatistics = ({ depositFeeCollected, withdrawFeeCollected }) =>
{depositFeeCollected.shouldDisplay && withdrawFeeCollected.shouldDisplay && <div className="separator" />}
{withdrawFeeCollected.shouldDisplay && (
<DataBlock
description="Withdrawal Fees"
description={withdrawFeeCollected.title ? withdrawFeeCollected.title : 'Withdrawal Fees'}
value={numeral(withdrawFeeCollected.value).format('0,0.00 a', Math.floor)}
type={withdrawFeeCollected.type}
dataTestid="withdrawal-fees-block"

@ -25,11 +25,12 @@ export const NetworkDetails = ({
}) => {
const networkTitle = isHome ? 'Bridge Home' : 'Bridge Foreign'
const logoClass = isHome ? 'home-logo home-logo-modal' : 'foreign-logo foreign-logo-modal'
const totalTitle = isHome
? nativeSupplyTitle
? `Native Coins Amount`
: `Totally minted by the bridge`
: `${currency} Tokens Amount`
const totalTitle =
isHome && !displayTokenAddress
? nativeSupplyTitle
? `Native Coins Amount`
: `Totally minted by the bridge`
: `${currency} Tokens Amount`
const totalAmount = isHome ? totalBalance : totalSupply
const formattedBalance = isNaN(numeral(balance).format('0.00', Math.floor))
? numeral(0).format('0,0.00', Math.floor)

@ -1,6 +1,6 @@
import React from 'react'
import yn from './utils/yn'
import { BRIDGE_MODES } from '../../../commons'
import { BRIDGE_MODES, isErcToErcMode } from '../../../commons'
import { BridgeStatistics } from './index'
import { Redirect } from 'react-router'
import { TransactionsStatistics } from './TransactionsStatistics'
@ -12,9 +12,10 @@ import { FeeStatistics } from './FeeStatistics'
export class StatisticsPage extends React.Component {
render() {
const { homeStore, foreignStore, bridgeMode, web3Store } = this.props.RootStore
const statisticsReady = homeStore.statistics.finished && foreignStore.statistics.finished
const isNativeToErc = bridgeMode === BRIDGE_MODES.NATIVE_TO_ERC
const leftTitle = isNativeToErc ? 'Deposits' : 'Withdraws'
const rightTitle = isNativeToErc ? 'Withdraws' : 'Deposits'
const leftTitle = isNativeToErc ? 'Deposits' : 'Withdrawals'
const rightTitle = isNativeToErc ? 'Withdrawals' : 'Deposits'
const { REACT_APP_UI_HOME_WITHOUT_EVENTS: HOME, REACT_APP_UI_FOREIGN_WITHOUT_EVENTS: FOREIGN } = process.env
const withoutEvents = web3Store.metamaskNet.id === web3Store.homeNet.id.toString() ? yn(HOME) : yn(FOREIGN)
@ -27,13 +28,14 @@ export class StatisticsPage extends React.Component {
<div className="statistics-bridge-container">
<span className="statistics-bridge-title statistics-title">Bridge Statistics</span>
<BridgeStatistics
users={homeStore.statistics.finished ? homeStore.statistics.users.size : ''}
totalBridged={homeStore.statistics.finished ? homeStore.statistics.totalBridged.toString() : ''}
users={statisticsReady ? homeStore.statistics.users.size : ''}
totalBridged={statisticsReady ? homeStore.statistics.totalBridged.toString() : ''}
homeBalance={homeStore.balance}
homeSymbol={homeStore.symbol}
homeNativeSupplyTitle={isNativeToErc}
foreignSymbol={foreignStore.symbol}
foreignSupply={foreignStore.totalSupply}
displayNetworkTokenSupply={isErcToErcMode(bridgeMode)}
/>
</div>
{homeStore.depositFeeCollected.finished &&
@ -48,17 +50,17 @@ export class StatisticsPage extends React.Component {
<div className="statistics-deposit-container">
<span className="statistics-deposit-title statistics-title">Tokens {leftTitle}</span>
<TransactionsStatistics
txNumber={homeStore.statistics.finished ? homeStore.statistics.deposits : ''}
txNumber={statisticsReady ? homeStore.statistics.deposits : ''}
type={foreignStore.symbol}
value={homeStore.statistics.finished ? homeStore.statistics.depositsValue : ''}
value={statisticsReady ? homeStore.statistics.depositsValue : ''}
/>
</div>
<div className="statistics-withdraw-container">
<span className="statistics-withdraw-title statistics-title">Tokens {rightTitle}</span>
<TransactionsStatistics
txNumber={homeStore.statistics.finished ? homeStore.statistics.withdraws : ''}
txNumber={statisticsReady ? homeStore.statistics.withdrawals : ''}
type={foreignStore.symbol}
value={homeStore.statistics.finished ? homeStore.statistics.withdrawsValue : ''}
value={statisticsReady ? homeStore.statistics.withdrawalsValue : ''}
/>
</div>
</div>

@ -10,7 +10,9 @@ import {
getBridgeABIs,
getTokenType,
ERC20_BYTES32_ABI,
getDeployedAtBlock
getDeployedAtBlock,
isMediatorMode,
HOME_V1_ABI
} from '../../../commons'
import {
getMaxPerTxLimit,
@ -103,6 +105,11 @@ class ForeignStore {
@observable
tokenType = ''
@observable
statistics = {
finished: false
}
feeManager = {
totalFeeDistributedFromSignatures: BN(0),
totalFeeDistributedFromAffirmation: BN(0)
@ -142,6 +149,7 @@ class ForeignStore {
this.getCurrentLimit()
this.getFee()
this.getValidators()
this.getStatistics()
this.getFeeEvents()
setInterval(() => {
this.getBlockNumber()
@ -239,58 +247,62 @@ class ForeignStore {
@action
async getEvents(fromBlock, toBlock) {
try {
fromBlock = fromBlock || this.filteredBlockNumber || this.latestBlockNumber - 50
toBlock = toBlock || this.filteredBlockNumber || 'latest'
fromBlock = fromBlock || this.filteredBlockNumber || this.latestBlockNumber - 50
toBlock = toBlock || this.filteredBlockNumber || 'latest'
if (fromBlock < 0) {
fromBlock = 0
}
if (fromBlock < 0) {
fromBlock = 0
}
let foreignEvents = await getPastEvents(this.foreignBridge, fromBlock, toBlock).catch(e => {
console.error("Couldn't get events", e)
return []
})
if (!this.filter) {
this.events = foreignEvents
}
if (this.waitingForConfirmation.size) {
const confirmationEvents = foreignEvents.filter(
event =>
event.event === 'RelayedMessage' && this.waitingForConfirmation.has(event.returnValues.transactionHash)
)
confirmationEvents.forEach(async event => {
const TxReceipt = await this.getTxReceipt(event.transactionHash)
if (TxReceipt && TxReceipt.logs && TxReceipt.logs.length > 1 && this.waitingForConfirmation.size) {
this.alertStore.setLoadingStepIndex(3)
const urlExplorer = this.getExplorerTxUrl(event.transactionHash)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitForeign
setTimeout(() => {
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.networkName} on Tx
<a href='${urlExplorer}' target='blank' style="overflow-wrap: break-word;word-wrap: break-word;">
${event.transactionHash}</a>`,
this.alertStore.FOREIGN_TRANSFER_SUCCESS
)
}, 2000)
this.waitingForConfirmation.delete(event.returnValues.transactionHash)
}
if (!isMediatorMode(this.rootStore.bridgeMode)) {
try {
let foreignEvents = await getPastEvents(this.foreignBridge, fromBlock, toBlock).catch(e => {
console.error("Couldn't get events", e)
return []
})
if (confirmationEvents.length) {
removePendingTransaction()
if (!this.filter) {
this.events = foreignEvents
}
}
return foreignEvents
} catch (e) {
this.alertStore.pushError(
`Cannot establish connection to Foreign Network.\n
if (this.waitingForConfirmation.size) {
const confirmationEvents = foreignEvents.filter(
event =>
event.event === 'RelayedMessage' && this.waitingForConfirmation.has(event.returnValues.transactionHash)
)
confirmationEvents.forEach(async event => {
const TxReceipt = await this.getTxReceipt(event.transactionHash)
if (TxReceipt && TxReceipt.logs && TxReceipt.logs.length > 1 && this.waitingForConfirmation.size) {
this.alertStore.setLoadingStepIndex(3)
const urlExplorer = this.getExplorerTxUrl(event.transactionHash)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitForeign
setTimeout(() => {
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.networkName} on Tx
<a href='${urlExplorer}' target='blank' style="overflow-wrap: break-word;word-wrap: break-word;">
${event.transactionHash}</a>`,
this.alertStore.FOREIGN_TRANSFER_SUCCESS
)
}, 2000)
this.waitingForConfirmation.delete(event.returnValues.transactionHash)
}
})
if (confirmationEvents.length) {
removePendingTransaction()
}
}
return foreignEvents
} catch (e) {
this.alertStore.pushError(
`Cannot establish connection to Foreign Network.\n
Please make sure you have set it up in env variables`,
this.alertStore.FOREIGN_CONNECTION_ERROR
)
this.alertStore.FOREIGN_CONNECTION_ERROR
)
}
} else {
this.detectMediatorTransferFinished(fromBlock, toBlock)
}
}
@ -379,30 +391,68 @@ class ForeignStore {
@action
async getValidators() {
try {
const foreignValidatorsAddress = await getValidatorContract(this.foreignBridge)
this.foreignBridgeValidators = new this.foreignWeb3.eth.Contract(BRIDGE_VALIDATORS_ABI, foreignValidatorsAddress)
if (!isMediatorMode(this.rootStore.bridgeMode)) {
try {
const foreignValidatorsAddress = await getValidatorContract(this.foreignBridge)
this.foreignBridgeValidators = new this.foreignWeb3.eth.Contract(
BRIDGE_VALIDATORS_ABI,
foreignValidatorsAddress
)
this.requiredSignatures = await getRequiredSignatures(this.foreignBridgeValidators)
this.validatorsCount = await getValidatorCount(this.foreignBridgeValidators)
this.requiredSignatures = await getRequiredSignatures(this.foreignBridgeValidators)
this.validatorsCount = await getValidatorCount(this.foreignBridgeValidators)
this.validators = await getValidatorList(foreignValidatorsAddress, this.foreignWeb3.eth)
} catch (e) {
console.error(e)
this.validators = await getValidatorList(foreignValidatorsAddress, this.foreignWeb3.eth)
} catch (e) {
console.error(e)
}
}
}
async getFeeEvents() {
async getStatistics() {
try {
const deployedAtBlock = await getDeployedAtBlock(this.foreignBridge)
const events = await getPastEvents(this.foreignBridge, deployedAtBlock, 'latest')
processLargeArrayAsync(events, this.processEvent, () => {
this.feeEventsFinished = true
})
if (isMediatorMode(this.rootStore.bridgeMode)) {
const events = await getPastEvents(this.foreignBridge, 0, 'latest', 'TokensBridged')
processLargeArrayAsync(events, this.processMediatorEvent, () => {
this.statistics.finished = true
this.rootStore.homeStore.statistics.totalBridged = this.rootStore.homeStore.statistics.totalBridged.plus(
this.rootStore.homeStore.statistics.withdrawalsValue
)
})
} else {
this.statistics.finished = true
}
} catch (e) {
console.error(e)
this.getFeeEvents()
this.getStatistics()
}
}
processMediatorEvent = event => {
if (event.returnValues && event.returnValues.recipient) {
this.rootStore.homeStore.statistics.users.add(event.returnValues.recipient)
}
this.rootStore.homeStore.statistics.withdrawals++
this.rootStore.homeStore.statistics.withdrawalsValue = this.rootStore.homeStore.statistics.withdrawalsValue.plus(
BN(fromDecimals(event.returnValues.value, this.tokenDecimals))
)
}
async getFeeEvents() {
if (!isMediatorMode(this.rootStore.bridgeMode)) {
try {
const deployedAtBlock = await getDeployedAtBlock(this.foreignBridge)
const events = await getPastEvents(this.foreignBridge, deployedAtBlock, 'latest')
processLargeArrayAsync(events, this.processEvent, () => {
this.feeEventsFinished = true
})
} catch (e) {
console.error(e)
this.getFeeEvents()
}
} else {
this.feeEventsFinished = true
}
}
@ -417,6 +467,33 @@ class ForeignStore {
)
}
}
async detectMediatorTransferFinished(fromBlock, toBlock) {
if (this.waitingForConfirmation.size > 0) {
try {
const events = await getPastEvents(this.foreignBridge, fromBlock, toBlock, 'TokensBridged')
const confirmationEvents = events.filter(event => this.waitingForConfirmation.has(event.returnValues.messageId))
if (confirmationEvents.length > 0) {
const event = confirmationEvents[0]
this.alertStore.setLoadingStepIndex(3)
const urlExplorer = this.getExplorerTxUrl(event.transactionHash)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitForeign
this.waitingForConfirmation.delete(event.returnValues.messageId)
removePendingTransaction()
setTimeout(() => {
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.networkName} on Tx
<a href='${urlExplorer}' target='blank' style="overflow-wrap: break-word;word-wrap: break-word;">
${event.transactionHash}</a>`,
this.alertStore.FOREIGN_TRANSFER_SUCCESS
)
}, 2000)
}
} catch (e) {
console.log(e)
}
}
}
}
export default ForeignStore

@ -12,7 +12,9 @@ import {
getBridgeABIs,
HOME_V1_ABI,
ERC20_BYTES32_ABI,
getDeployedAtBlock
getDeployedAtBlock,
isErcToErcMode,
isMediatorMode
} from '../../../commons'
import {
getMaxPerTxLimit,
@ -37,7 +39,8 @@ import {
getBlockRewardContract,
getValidatorContract,
getRequiredSignatures,
getValidatorCount
getValidatorCount,
getFee
} from './utils/contract'
import { balanceLoaded, removePendingTransaction } from './utils/testUtils'
import sleep from './utils/sleep'
@ -113,19 +116,23 @@ class HomeStore {
statistics = {
deposits: 0,
depositsValue: BN(0),
withdraws: 0,
withdrawsValue: BN(0),
withdrawals: 0,
withdrawalsValue: BN(0),
totalBridged: BN(0),
users: new Set(),
finished: false
}
@observable
feeEventsFinished = false
@observable
depositFeeCollected = {
value: BN(0),
type: '',
shouldDisplay: false,
finished: false
finished: false,
title: ''
}
@observable
@ -133,7 +140,8 @@ class HomeStore {
value: BN(0),
type: '',
shouldDisplay: false,
finished: false
finished: false,
title: ''
}
feeManager = {
@ -166,7 +174,7 @@ class HomeStore {
}
const { HOME_ABI } = getBridgeABIs(this.rootStore.bridgeMode)
this.homeBridge = new this.homeWeb3.eth.Contract(HOME_ABI, this.COMMON_HOME_BRIDGE_ADDRESS)
if (this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_ERC) {
if (isErcToErcMode(this.rootStore.bridgeMode)) {
await this.getTokenInfo()
} else if (this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE) {
await this.getBlockRewardContract()
@ -180,6 +188,7 @@ class HomeStore {
this.getFee()
this.getValidators()
this.getStatistics()
this.getFeeEvents()
this.calculateCollectedFees()
setInterval(() => {
this.getEvents()
@ -194,8 +203,6 @@ class HomeStore {
try {
this.tokenAddress = await getErc677TokenAddress(this.homeBridge)
this.tokenContract = new this.homeWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, this.tokenAddress)
this.symbol = await getSymbol(this.tokenContract)
this.tokenName = await getName(this.tokenContract)
const alternativeContract = new this.homeWeb3.eth.Contract(ERC20_BYTES32_ABI, this.tokenAddress)
try {
this.symbol = await getSymbol(this.tokenContract)
@ -243,7 +250,7 @@ class HomeStore {
@action
async getBalance() {
try {
if (this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_ERC) {
if (isErcToErcMode(this.rootStore.bridgeMode)) {
this.balance = await getTotalSupply(this.tokenContract)
this.web3Store.getWeb3Promise.then(async () => {
this.userBalance = await getBalanceOf(this.tokenContract, this.web3Store.defaultAccount.address)
@ -264,18 +271,28 @@ class HomeStore {
@action
async getFee() {
const feeManager = await getFeeManager(this.homeBridge)
if (feeManager !== ZERO_ADDRESS) {
const feeManagerModeHash = await getFeeManagerMode(this.homeBridge)
this.feeManager.feeManagerMode = decodeFeeManagerMode(feeManagerModeHash)
if (!isMediatorMode(this.rootStore.bridgeMode)) {
const feeManager = await getFeeManager(this.homeBridge)
if (feeManager !== ZERO_ADDRESS) {
const feeManagerModeHash = await getFeeManagerMode(this.homeBridge)
this.feeManager.feeManagerMode = decodeFeeManagerMode(feeManagerModeHash)
if (this.feeManager.feeManagerMode === FEE_MANAGER_MODE.BOTH_DIRECTIONS) {
this.feeManager.homeFee = await getHomeFee(this.homeBridge)
this.feeManager.foreignFee = await getForeignFee(this.homeBridge)
if (this.feeManager.feeManagerMode === FEE_MANAGER_MODE.BOTH_DIRECTIONS) {
this.feeManager.homeFee = await getHomeFee(this.homeBridge)
this.feeManager.foreignFee = await getForeignFee(this.homeBridge)
} else {
this.feeManager.homeFee = new BN(0)
this.feeManager.foreignFee = await getForeignFee(this.homeBridge)
}
} else {
this.feeManager.feeManagerMode = FEE_MANAGER_MODE.UNDEFINED
this.feeManager.homeFee = new BN(0)
this.feeManager.foreignFee = await getForeignFee(this.homeBridge)
this.feeManager.foreignFee = new BN(0)
}
} else if (this.rootStore.bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC) {
this.feeManager.feeManagerMode = FEE_MANAGER_MODE.ONE_DIRECTION_STAKE
this.feeManager.homeFee = await getFee(this.homeBridge)
this.feeManager.foreignFee = new BN(0)
} else {
this.feeManager.feeManagerMode = FEE_MANAGER_MODE.UNDEFINED
this.feeManager.homeFee = new BN(0)
@ -285,64 +302,68 @@ class HomeStore {
@action
async getEvents(fromBlock, toBlock) {
try {
fromBlock = fromBlock || this.filteredBlockNumber || this.latestBlockNumber - 50
toBlock = toBlock || this.filteredBlockNumber || 'latest'
fromBlock = fromBlock || this.filteredBlockNumber || this.latestBlockNumber - 50
toBlock = toBlock || this.filteredBlockNumber || 'latest'
if (fromBlock < 0) {
fromBlock = 0
}
if (fromBlock < 0) {
fromBlock = 0
}
let events = await getPastEvents(this.homeBridge, fromBlock, toBlock).catch(e => {
console.error("Couldn't get events", e)
return []
})
let homeEvents = []
await asyncForEach(events, async event => {
if (event.event === 'SignedForUserRequest' || event.event === 'CollectedSignatures') {
event.signedTxHash = await this.getSignedTx(event.returnValues.messageHash)
}
homeEvents.push(event)
})
if (!this.filter) {
this.events = homeEvents
}
if (this.waitingForConfirmation.size) {
const confirmationEvents = homeEvents.filter(
event =>
event.event === 'AffirmationCompleted' &&
this.waitingForConfirmation.has(event.returnValues.transactionHash)
)
confirmationEvents.forEach(event => {
this.alertStore.setLoadingStepIndex(3)
const urlExplorer = this.getExplorerTxUrl(event.transactionHash)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitHome
setTimeout(() => {
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.networkName} on Tx
<a href='${urlExplorer}' target='blank' style="overflow-wrap: break-word;word-wrap: break-word;">
${event.transactionHash}</a>`,
this.alertStore.HOME_TRANSFER_SUCCESS
)
}, 2000)
this.waitingForConfirmation.delete(event.returnValues.transactionHash)
if (!isMediatorMode(this.rootStore.bridgeMode)) {
try {
let events = await getPastEvents(this.homeBridge, fromBlock, toBlock).catch(e => {
console.error("Couldn't get events", e)
return []
})
if (confirmationEvents.length) {
removePendingTransaction()
}
}
let homeEvents = []
await asyncForEach(events, async event => {
if (event.event === 'SignedForUserRequest' || event.event === 'CollectedSignatures') {
event.signedTxHash = await this.getSignedTx(event.returnValues.messageHash)
}
homeEvents.push(event)
})
return homeEvents
} catch (e) {
this.alertStore.pushError(
`Cannot establish connection to Home Network.\n
if (!this.filter) {
this.events = homeEvents
}
if (this.waitingForConfirmation.size) {
const confirmationEvents = homeEvents.filter(
event =>
event.event === 'AffirmationCompleted' &&
this.waitingForConfirmation.has(event.returnValues.transactionHash)
)
confirmationEvents.forEach(event => {
this.alertStore.setLoadingStepIndex(3)
const urlExplorer = this.getExplorerTxUrl(event.transactionHash)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitHome
setTimeout(() => {
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.networkName} on Tx
<a href='${urlExplorer}' target='blank' style="overflow-wrap: break-word;word-wrap: break-word;">
${event.transactionHash}</a>`,
this.alertStore.HOME_TRANSFER_SUCCESS
)
}, 2000)
this.waitingForConfirmation.delete(event.returnValues.transactionHash)
})
if (confirmationEvents.length) {
removePendingTransaction()
}
}
return homeEvents
} catch (e) {
this.alertStore.pushError(
`Cannot establish connection to Home Network.\n
Please make sure you have set it up in env variables`,
this.alertStore.HOME_CONNECTION_ERROR
)
this.alertStore.HOME_CONNECTION_ERROR
)
}
} else {
this.detectMediatorTransferFinished(fromBlock, toBlock)
}
}
@ -408,30 +429,40 @@ class HomeStore {
@action
async getValidators() {
try {
const homeValidatorsAddress = await getValidatorContract(this.homeBridge)
this.homeBridgeValidators = new this.homeWeb3.eth.Contract(BRIDGE_VALIDATORS_ABI, homeValidatorsAddress)
if (!isMediatorMode(this.rootStore.bridgeMode)) {
try {
const homeValidatorsAddress = await getValidatorContract(this.homeBridge)
this.homeBridgeValidators = new this.homeWeb3.eth.Contract(BRIDGE_VALIDATORS_ABI, homeValidatorsAddress)
this.requiredSignatures = await getRequiredSignatures(this.homeBridgeValidators)
this.validatorsCount = await getValidatorCount(this.homeBridgeValidators)
this.requiredSignatures = await getRequiredSignatures(this.homeBridgeValidators)
this.validatorsCount = await getValidatorCount(this.homeBridgeValidators)
this.validators = await getValidatorList(homeValidatorsAddress, this.homeWeb3.eth)
} catch (e) {
console.error(e)
this.validators = await getValidatorList(homeValidatorsAddress, this.homeWeb3.eth)
} catch (e) {
console.error(e)
}
}
}
async getStatistics() {
try {
const deployedAtBlock = await getDeployedAtBlock(this.homeBridge)
const { HOME_ABI } = getBridgeABIs(this.rootStore.bridgeMode)
const abi = [...HOME_V1_ABI, ...HOME_ABI]
const contract = new this.homeWeb3.eth.Contract(abi, this.COMMON_HOME_BRIDGE_ADDRESS)
const events = await getPastEvents(contract, deployedAtBlock, 'latest')
processLargeArrayAsync(events, this.processEvent, () => {
this.statistics.finished = true
this.statistics.totalBridged = this.statistics.depositsValue.plus(this.statistics.withdrawsValue)
})
if (!isMediatorMode(this.rootStore.bridgeMode)) {
const deployedAtBlock = await getDeployedAtBlock(this.homeBridge)
const { HOME_ABI } = getBridgeABIs(this.rootStore.bridgeMode)
const abi = [...HOME_V1_ABI, ...HOME_ABI]
const contract = new this.homeWeb3.eth.Contract(abi, this.COMMON_HOME_BRIDGE_ADDRESS)
const events = await getPastEvents(contract, deployedAtBlock, 'latest')
processLargeArrayAsync(events, this.processEvent, () => {
this.statistics.finished = true
this.statistics.totalBridged = this.statistics.totalBridged.plus(this.statistics.depositsValue)
})
} else {
const events = await getPastEvents(this.homeBridge, 0, 'latest', 'TokensBridged')
processLargeArrayAsync(events, this.processMediatorEvent, () => {
this.statistics.finished = true
this.statistics.totalBridged = this.statistics.depositsValue.plus(this.statistics.withdrawalsValue)
})
}
} catch (e) {
console.error(e)
this.getStatistics()
@ -448,8 +479,8 @@ class HomeStore {
BN(fromDecimals(event.returnValues.value, this.tokenDecimals))
)
} else if (event.event === 'AffirmationCompleted' || event.event === 'Withdraw') {
this.statistics.withdraws++
this.statistics.withdrawsValue = this.statistics.withdrawsValue.plus(
this.statistics.withdrawals++
this.statistics.withdrawalsValue = this.statistics.withdrawalsValue.plus(
BN(fromDecimals(event.returnValues.value, this.tokenDecimals))
)
} else if (event.event === 'FeeDistributedFromSignatures') {
@ -463,9 +494,49 @@ class HomeStore {
}
}
processMediatorEvent = event => {
if (event.returnValues && event.returnValues.recipient) {
this.statistics.users.add(event.returnValues.recipient)
}
this.statistics.deposits++
this.statistics.depositsValue = this.statistics.depositsValue.plus(
BN(fromDecimals(event.returnValues.value, this.tokenDecimals))
)
}
async getFeeEvents() {
try {
if (this.rootStore.bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC) {
const blockRewardAddress = await getBlockRewardContract(this.homeBridge)
const blockRewardContract = new this.homeWeb3.eth.Contract(BLOCK_REWARD_ABI, blockRewardAddress)
const events = await getPastEvents(blockRewardContract, 0, 'latest', 'BridgeTokenRewardAdded', {
filter: {
bridge: this.COMMON_HOME_BRIDGE_ADDRESS
}
})
processLargeArrayAsync(events, this.processFeeEvents, () => {
this.feeEventsFinished = true
})
} else {
this.feeEventsFinished = true
}
} catch (e) {
console.error(e)
this.getFeeEvents()
}
}
processFeeEvents = event => {
this.feeManager.totalFeeDistributedFromSignatures = this.feeManager.totalFeeDistributedFromSignatures.plus(
BN(fromDecimals(event.returnValues.amount, this.tokenDecimals))
)
}
calculateCollectedFees() {
if (
!this.statistics.finished ||
!this.feeEventsFinished ||
!this.rootStore.foreignStore.feeEventsFinished ||
!this.feeManager.feeManagerMode ||
!this.rootStore.foreignStore.feeManager.feeManagerMode
@ -493,6 +564,10 @@ class HomeStore {
? this.feeManager.totalFeeDistributedFromAffirmation
: this.rootStore.foreignStore.feeManager.totalFeeDistributedFromAffirmation
if (this.rootStore.bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC) {
this.depositFeeCollected.title = 'Withdrawal Fees'
}
this.depositFeeCollected.finished = true
this.withdrawFeeCollected.finished = true
}
@ -502,9 +577,7 @@ class HomeStore {
}
getDisplayedBalance() {
return this.rootStore.bridgeMode === BRIDGE_MODES.ERC_TO_ERC
? this.userBalance
: this.web3Store.defaultAccount.homeBalance
return isErcToErcMode(this.rootStore.bridgeMode) ? this.userBalance : this.web3Store.defaultAccount.homeBalance
}
async getBlockRewardContract() {
@ -527,6 +600,33 @@ class HomeStore {
return sleep(5000).then(() => this.waitUntilProcessed(txHash, value))
}
}
async detectMediatorTransferFinished(fromBlock, toBlock) {
if (this.waitingForConfirmation.size > 0) {
try {
const events = await getPastEvents(this.homeBridge, fromBlock, toBlock, 'TokensBridged')
const confirmationEvents = events.filter(event => this.waitingForConfirmation.has(event.returnValues.messageId))
if (confirmationEvents.length > 0) {
const event = events[0]
this.alertStore.setLoadingStepIndex(3)
const urlExplorer = this.getExplorerTxUrl(event.transactionHash)
const unitReceived = getUnit(this.rootStore.bridgeMode).unitHome
this.waitingForConfirmation.delete(event.returnValues.messageId)
removePendingTransaction()
setTimeout(() => {
this.alertStore.pushSuccess(
`${unitReceived} received on ${this.networkName} on Tx
<a href='${urlExplorer}' target='blank' style="overflow-wrap: break-word;word-wrap: break-word;">
${event.transactionHash}</a>`,
this.alertStore.HOME_TRANSFER_SUCCESS
)
}, 2000)
}
} catch (e) {
console.log(e)
}
}
}
}
export default HomeStore

@ -27,8 +27,8 @@ export const getCurrentLimit = async (contract, decimals) => {
}
}
export const getPastEvents = (contract, fromBlock, toBlock, event = 'allEvents') =>
commonGetPastEvents(contract, { fromBlock, toBlock, event })
export const getPastEvents = (contract, fromBlock, toBlock, event = 'allEvents', options = {}) =>
commonGetPastEvents(contract, { fromBlock, toBlock, event, options })
export const getErc677TokenAddress = contract => contract.methods.erc677token().call()
@ -86,6 +86,15 @@ export const getForeignFee = async contract => {
return new BN(fromWei(feeInWei.toString()))
}
export const getFee = async contract => {
try {
const feeInWei = await contract.methods.getFee().call()
return new BN(fromWei(feeInWei.toString()))
} catch (e) {
return new BN(0)
}
}
export const getBlockRewardContract = contract => contract.methods.blockRewardContract().call()
export const getValidatorContract = contract => contract.methods.validatorContract().call()

@ -65,6 +65,15 @@ export const getRewardableData = (homeFeeManager, foreignFeeManager) => {
displayDeposit: true,
displayWithdraw: false
}
} else if (homeFeeManager.feeManagerMode === FEE_MANAGER_MODE.ONE_DIRECTION_STAKE) {
return {
homeFee: homeFeeManager.homeFee,
foreignFee: new BN(0),
depositSymbol: 'home',
withdrawSymbol: '',
displayDeposit: true,
displayWithdraw: false
}
} else {
return {
homeFee: new BN(0),