Merge the develop branch to the master branch, preparation to v2.7.0-rc2

This merge contains the following set of changes:
  * [Monitor, Improvement] Include stats about failed and pending messages in mediators monitor endpoint (#544), closes #522
  * [Oracle, Fix] Fix remote shutdown sender behaviour (#550)
  * [Oracle, Fix] Force update of gas price when getting underprice tx error (#554), closes #545
  * [Oracle, Fix] Add oracle configuration option for max block range length (#557)
  * [Monitor, Fix] Limit block range of logs request (#543), closes #537
  * [Monitor, Fix] Add missing cache file save (#549)
  * [UI, Other] Remove UI from monorepo (#553)
  * [Common, Other] Update the contract's submodule to the release 5.7.0-rc0 (#548)
  * [Common, Other] Final version of the FT OB contract security audit report by ChainSecurity (#559)
This commit is contained in:
Alexander Kolotov 2021-05-04 07:42:06 -06:00 committed by GitHub
commit 9da1d7ab0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
311 changed files with 394 additions and 13340 deletions

@ -61,31 +61,6 @@ jobs:
key: ${{ needs.initialize.outputs.cache_key }} key: ${{ needs.initialize.outputs.cache_key }}
- name: yarn run ${{ matrix.task }} - name: yarn run ${{ matrix.task }}
run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }} run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn run ${{ matrix.task }}
ui-coverage:
runs-on: ubuntu-latest
needs:
- initialize
if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags')
steps:
- uses: actions/setup-node@v1
with:
node-version: 10
- uses: actions/checkout@v2
with:
submodules: true
- uses: actions/cache@v2
id: cache-repo
with:
path: |
**/node_modules
contracts/build
key: ${{ needs.initialize.outputs.cache_key }}
- name: yarn workspace ui run coverage
run: ${{ steps.cache-repo.outputs.cache-hit }} && yarn workspace ui run coverage
- uses: coverallsapp/github-action@master
with:
github-token: ${{ github.token }}
path-to-lcov: ./ui/coverage/lcov.info
build-e2e-images: build-e2e-images:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@ -98,7 +73,6 @@ jobs:
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
- name: Rebuild and push updated images - name: Rebuild and push updated images
run: | run: |
@ -109,7 +83,6 @@ jobs:
if ! check_if_image_exists e2e ${E2E_TAG}; then updated+=("e2e"); fi if ! check_if_image_exists e2e ${E2E_TAG}; then updated+=("e2e"); fi
if ! check_if_image_exists oracle ${ORACLE_TAG}; then updated+=("oracle"); fi if ! check_if_image_exists oracle ${ORACLE_TAG}; then updated+=("oracle"); fi
if ! check_if_image_exists monitor ${MONITOR_TAG}; then updated+=("monitor"); fi if ! check_if_image_exists monitor ${MONITOR_TAG}; then updated+=("monitor"); fi
if ! check_if_image_exists ui ${UI_TAG}; then updated+=("ui"); fi
if ! check_if_image_exists alm ${ALM_TAG}; then updated+=("alm"); fi if ! check_if_image_exists alm ${ALM_TAG}; then updated+=("alm"); fi
if [ ${#updated[@]} -gt 0 ]; then if [ ${#updated[@]} -gt 0 ]; then
echo "Updated services: ${updated[@]}" echo "Updated services: ${updated[@]}"
@ -149,12 +122,10 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
task: [oracle-e2e, monitor-e2e, alm-e2e, 'ui-e2e:ci'] task: [oracle-e2e, monitor-e2e, alm-e2e]
include: include:
- task: alm-e2e - task: alm-e2e
use-cache: true use-cache: true
- task: 'ui-e2e:ci'
use-cache: true
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
@ -165,7 +136,6 @@ jobs:
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
- if: ${{ matrix.use-cache }} - if: ${{ matrix.use-cache }}
uses: actions/cache@v2 uses: actions/cache@v2
@ -187,7 +157,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
task: [oracle, ui, monitor, multiple, repo] task: [oracle, monitor, multiple, repo]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
@ -206,20 +176,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
task: [amb, erc-to-erc, erc-to-native, native-to-erc, amb-stake-erc-to-erc] task: [amb, erc-to-erc, erc-to-native, native-to-erc]
include:
- task: erc-to-erc
ui-e2e-grep: 'ERC TO ERC'
ui-config: 'e2e-commons/components-envs/ui-erc20.env'
- task: erc-to-native
ui-e2e-grep: 'ERC TO NATIVE'
ui-config: 'e2e-commons/components-envs/ui-erc20-native.env'
- task: native-to-erc
ui-e2e-grep: 'NATIVE TO ERC'
ui-config: 'e2e-commons/components-envs/ui.env'
- task: amb-stake-erc-to-erc
ui-e2e-grep: 'AMB-STAKE-ERC-TO-ERC'
ui-config: 'e2e-commons/components-envs/ui-amb-stake-erc20-erc20.env'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
@ -230,7 +187,6 @@ jobs:
echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV echo "E2E_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'Dockerfile.e2e', 'commons', 'oracle-e2e', 'monitor-e2e') }}" >> $GITHUB_ENV
echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV echo "ORACLE_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'oracle') }}" >> $GITHUB_ENV
echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV echo "MONITOR_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'monitor') }}" >> $GITHUB_ENV
echo "UI_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'ui') }}" >> $GITHUB_ENV
echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV echo "ALM_TAG=${{ hashFiles('yarn.lock', 'package.json', 'submodule.status', 'commons', 'alm') }}" >> $GITHUB_ENV
echo "MOLECULE_RUNNER_TAG=${{ hashFiles('./deployment-e2e/Dockerfile') }}" >> $GITHUB_ENV echo "MOLECULE_RUNNER_TAG=${{ hashFiles('./deployment-e2e/Dockerfile') }}" >> $GITHUB_ENV
- uses: actions/cache@v2 - uses: actions/cache@v2
@ -248,24 +204,9 @@ jobs:
run: | run: |
docker-compose -f ./e2e-commons/docker-compose.yml pull oracle docker-compose -f ./e2e-commons/docker-compose.yml pull oracle
docker tag ${DOCKER_IMAGE_BASE}/tokenbridge-e2e-oracle:${ORACLE_TAG} poanetwork/tokenbridge-oracle:latest docker tag ${DOCKER_IMAGE_BASE}/tokenbridge-e2e-oracle:${ORACLE_TAG} poanetwork/tokenbridge-oracle:latest
- if: ${{ matrix.ui-e2e-grep }} - name: Deploy oracle
name: Pull e2e ui image
run: |
docker-compose -f ./e2e-commons/docker-compose.yml pull ui
docker build \
--build-arg DOCKER_IMAGE_BASE=${DOCKER_IMAGE_BASE} \
--build-arg UI_TAG=${UI_TAG} \
--build-arg DOT_ENV_PATH=${{ matrix.ui-config }} \
-f ./e2e-commons/Dockerfile.ui \
-t ui_ui:latest \
.
- name: Deploy oracle and ui
run: deployment-e2e/molecule.sh ultimate-${{ matrix.task }} run: deployment-e2e/molecule.sh ultimate-${{ matrix.task }}
- name: Reset docker socket permissions - name: Reset docker socket permissions
run: sudo chown -R $USER:docker /var/run/docker.sock run: sudo chown -R $USER:docker /var/run/docker.sock
- name: Run ui e2e tests
if: ${{ matrix.ui-e2e-grep }}
run: cd ui-e2e && xvfb-run yarn mocha -g "${{ matrix.ui-e2e-grep }}" -b ./test.js
- name: Run oracle e2e tests - name: Run oracle e2e tests
if: ${{ !matrix.ui-e2e-grep }} run: docker-compose -f ./e2e-commons/docker-compose.yml run -e ULTIMATE=true e2e yarn workspace oracle-e2e run ${{ matrix.task }}
run: docker-compose -f ./e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run ${{ matrix.task }}

@ -49,29 +49,8 @@ ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL | Optional interval in milliseconds use
ORACLE_SIDE_RPC_URL | Optional HTTPS URL(s) for communication with the external shutdown service or side RPC nodes, used for shutdown manager activities. Several URLs can be specified, delimited by spaces. If the connection to one of these nodes is lost the next URL is used for connection. | URL(s) ORACLE_SIDE_RPC_URL | Optional HTTPS URL(s) for communication with the external shutdown service or side RPC nodes, used for shutdown manager activities. Several URLs can be specified, delimited by spaces. If the connection to one of these nodes is lost the next URL is used for connection. | URL(s)
ORACLE_SHUTDOWN_CONTRACT_ADDRESS | Optional contract address in the side chain accessible through `ORACLE_SIDE_RPC_URL`, where the method passed in `ORACLE_SHUTDOWN_CONTRACT_METHOD` is implemented. | `address` ORACLE_SHUTDOWN_CONTRACT_ADDRESS | Optional contract address in the side chain accessible through `ORACLE_SIDE_RPC_URL`, where the method passed in `ORACLE_SHUTDOWN_CONTRACT_METHOD` is implemented. | `address`
ORACLE_SHUTDOWN_CONTRACT_METHOD | Method signature to be used in the side chain to identify the current shutdown status. Method should return boolean. Default value is `isShutdown()`. | `function signature` ORACLE_SHUTDOWN_CONTRACT_METHOD | Method signature to be used in the side chain to identify the current shutdown status. Method should return boolean. Default value is `isShutdown()`. | `function signature`
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Foreign chain. Infinite, if not provided. | `integer`
ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT | Max length for the block range used in `eth_getLogs` requests for polling contract events for the Home chain. Infinite, if not provided. | `integer`
## UI configuration
name | description | value
--- | --- | ---
UI_TITLE | The title for the bridge UI page. `%c` will be replaced by the name of the network. | string
UI_OG_TITLE | The meta title for the deployed bridge page. | string
UI_DESCRIPTION | The meta description for the deployed bridge page. | string
UI_NATIVE_TOKEN_DISPLAY_NAME | name of the home native coin | string
UI_HOME_NETWORK_DISPLAY_NAME | name to be displayed for home network | string
UI_FOREIGN_NETWORK_DISPLAY_NAME | name to be displayed for foreign network | string
UI_HOME_WITHOUT_EVENTS | `true` if home network doesn't support events | true/false
UI_FOREIGN_WITHOUT_EVENTS | `true` if foreign network doesn't support events | true/false
UI_HOME_EXPLORER_TX_TEMPLATE | template link to transaction on home explorer. `%s` will be replaced by transaction hash | URL template
UI_FOREIGN_EXPLORER_TX_TEMPLATE | template link to transaction on foreign explorer. `%s` will be replaced by transaction hash | URL template
UI_HOME_EXPLORER_ADDRESS_TEMPLATE | template link to address on home explorer. `%s` will be replaced by address | URL template
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE | template link to address on foreign explorer. `%s` will be replaced by address | URL template
UI_HOME_GAS_PRICE_UPDATE_INTERVAL | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Home Bridge contract. | integer
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Foreign Bridge contract. | integer
UI_PORT | The port for the UI app. | integer
UI_STYLES | The set of styles to render the bridge UI page. | core/classic/stake
UI_PUBLIC_URL | The public url for the deployed bridge page | string
## Monitor configuration ## Monitor configuration

@ -20,12 +20,10 @@ Sub-repositories maintained within this monorepo are listed below.
| Sub-repository | Description | | Sub-repository | Description |
| --- | --- | | --- | --- |
| [Oracle](oracle/README.md) | Oracle responsible for listening to bridge related events and authorizing asset transfers. | | [Oracle](oracle/README.md) | Oracle responsible for listening to bridge related events and authorizing asset transfers. |
| [UI](ui/README.md) | DApp interface to transfer tokens and coins between chains. |
| [Monitor](monitor/README.md) | Tool for checking balances and unprocessed events in bridged networks. | | [Monitor](monitor/README.md) | Tool for checking balances and unprocessed events in bridged networks. |
| [Deployment](deployment/README.md) | Ansible playbooks for deploying cross-chain bridges. | | [Deployment](deployment/README.md) | Ansible playbooks for deploying cross-chain bridges. |
| [Oracle-E2E](oracle-e2e/README.md) | End to end tests for the Oracle | | [Oracle-E2E](oracle-e2e/README.md) | End to end tests for the Oracle |
| [Monitor-E2E](monitor-e2e/README.md) | End to end tests for the Monitor | | [Monitor-E2E](monitor-e2e/README.md) | End to end tests for the Monitor |
| [UI-E2E](ui-e2e/README.md) | End to end tests for the UI |
| [Deployment-E2E](deployment-e2e/README.md) | End to end tests for the Deployment | | [Deployment-E2E](deployment-e2e/README.md) | End to end tests for the Deployment |
| [Commons](commons/README.md) | Interfaces, constants and utilities shared between the sub-repositories | | [Commons](commons/README.md) | Interfaces, constants and utilities shared between the sub-repositories |
| [E2E-Commons](e2e-commons/README.md) | Common utilities and configuration used in end to end tests | | [E2E-Commons](e2e-commons/README.md) | Common utilities and configuration used in end to end tests |
@ -68,7 +66,7 @@ Clone the repository:
git clone https://github.com/poanetwork/tokenbridge git clone https://github.com/poanetwork/tokenbridge
``` ```
If there is no need to build docker images for the TokenBridge components (oracle, monitor, UI), initialize submodules, install dependencies, compile the Smart Contracts: If there is no need to build docker images for the TokenBridge components (oracle, monitor), initialize submodules, install dependencies, compile the Smart Contracts:
``` ```
yarn initialize yarn initialize
``` ```
@ -91,7 +89,7 @@ Running tests for all projects:
yarn test yarn test
``` ```
Additionally there are end-to-end tests for [Oracle](oracle-e2e/README.md) and [UI](ui-e2e/README.md). Additionally there are end-to-end tests for [Oracle](oracle-e2e/README.md) and [Monitor](monitor-e2e/README.md).
For details on building, running and developing please refer to respective READMEs in sub-repositories. For details on building, running and developing please refer to respective READMEs in sub-repositories.

@ -1,4 +1,4 @@
const { toWei, toBN } = require('web3-utils') const { toWei, toBN, BN } = require('web3-utils')
const { GasPriceOracle } = require('gas-price-oracle') const { GasPriceOracle } = require('gas-price-oracle')
const { BRIDGE_MODES, FEE_MANAGER_MODE, ERC_TYPES } = require('./constants') const { BRIDGE_MODES, FEE_MANAGER_MODE, ERC_TYPES } = require('./constants')
const { REWARDABLE_VALIDATORS_ABI } = require('./abis') const { REWARDABLE_VALIDATORS_ABI } = require('./abis')
@ -150,11 +150,8 @@ const tryCall = async (method, fallbackValue) => {
const getDeployedAtBlock = async contract => tryCall(contract.methods.deployedAtBlock(), 0) const getDeployedAtBlock = async contract => tryCall(contract.methods.deployedAtBlock(), 0)
const getPastEvents = async ( const getPastEventsOrSplit = async (contract, { event, fromBlock, toBlock, options }) => {
contract, let events = []
{ event = 'allEvents', fromBlock = toBN(0), toBlock = 'latest', options = {} }
) => {
let events
try { try {
events = await contract.getPastEvents(event, { events = await contract.getPastEvents(event, {
...options, ...options,
@ -162,19 +159,19 @@ const getPastEvents = async (
toBlock toBlock
}) })
} catch (e) { } catch (e) {
if (e.message.includes('query returned more than') && toBlock !== 'latest') { if (e.message.includes('query returned more than') || e.message.toLowerCase().includes('timeout')) {
const middle = toBN(fromBlock) const middle = toBN(fromBlock)
.add(toBN(toBlock)) .add(toBN(toBlock))
.divRound(toBN(2)) .divRound(toBN(2))
const middlePlusOne = middle.add(toBN(1)) const middlePlusOne = middle.add(toBN(1))
const firstHalfEvents = await getPastEvents(contract, { const firstHalfEvents = await getPastEventsOrSplit(contract, {
options, options,
event, event,
fromBlock, fromBlock,
toBlock: middle toBlock: middle
}) })
const secondHalfEvents = await getPastEvents(contract, { const secondHalfEvents = await getPastEventsOrSplit(contract, {
options, options,
event, event,
fromBlock: middlePlusOne, fromBlock: middlePlusOne,
@ -188,6 +185,31 @@ const getPastEvents = async (
return events return events
} }
const getPastEvents = async (
contract,
{ event = 'allEvents', fromBlock = toBN(0), toBlock = 'latest', options = {} }
) => {
if (toBlock === 'latest') {
return contract.getPastEvents(event, {
...options,
fromBlock,
toBlock
})
}
const batchSize = 1000000
const to = toBN(toBlock)
const events = []
for (let from = toBN(fromBlock); from.lte(to); from = from.addn(batchSize + 1)) {
const opts = { event, fromBlock: from, toBlock: BN.min(to, from.addn(batchSize)), options }
const batch = await getPastEventsOrSplit(contract, opts)
events.push(batch)
}
return [].concat(...events)
}
const getValidatorList = async (address, eth, options) => { const getValidatorList = async (address, eth, options) => {
options.logger && options.logger.debug && options.logger.debug('getting validatorList') options.logger && options.logger.debug && options.logger.debug('getting validatorList')

@ -1 +1 @@
Subproject commit 835742dfd8f1c869d4e7b61582155d250d6cf094 Subproject commit c9377114f7bcf04cd12a30d9eca0a63362dcaedc

@ -16,7 +16,7 @@ Alternatively, if there are no changes except the playbooks, you can use the `ma
./molecule.sh <scenario_name> ./molecule.sh <scenario_name>
``` ```
In this case `master` branch will be used as a codebase for Monitor, UI, Oracle and Contracts deployed by your local playbook. In this case `master` branch will be used as a codebase for Monitor, Oracle and Contracts deployed by your local playbook.
## Run the tests ## Run the tests
@ -29,7 +29,6 @@ Available scenarios:
Scenario | Description Scenario | Description
--- | --- --- | ---
oracle | Deploys and checks standalone Oracle on Ubuntu host oracle | Deploys and checks standalone Oracle on Ubuntu host
ui | Deploys and checks standalone UI on Ubuntu host
## Ultimate E2E tests ## Ultimate E2E tests

@ -16,7 +16,6 @@ platforms:
children: children:
- oracle - oracle
- monitor - monitor
- ui
image: ubuntu:16.04 image: ubuntu:16.04
privileged: true privileged: true
network_mode: host network_mode: host

@ -8,7 +8,6 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
@pytest.mark.parametrize("service", [ @pytest.mark.parametrize("service", [
("poabridge"), ("poabridge"),
("tokenbridge-ui"),
("tokenbridge-monitor") ("tokenbridge-monitor")
]) ])
def test_services(host, service): def test_services(host, service):
@ -25,7 +24,6 @@ def test_services(host, service):
("oracle_bridge_senderhome_1"), ("oracle_bridge_senderhome_1"),
("oracle_bridge_senderforeign_1"), ("oracle_bridge_senderforeign_1"),
("oracle_bridge_shutdown_1"), ("oracle_bridge_shutdown_1"),
("ui_ui_1"),
("monitor_monitor_1") ("monitor_monitor_1")
]) ])
def test_docker_containers(host, name): def test_docker_containers(host, name):

@ -15,7 +15,6 @@ testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
("/home/poadocker/bridge/contracts"), ("/home/poadocker/bridge/contracts"),
("/home/poadocker/bridge/oracle"), ("/home/poadocker/bridge/oracle"),
("/home/poadocker/bridge/monitor"), ("/home/poadocker/bridge/monitor"),
("/home/poadocker/bridge/ui"),
("/home/poadocker/bridge/parity") ("/home/poadocker/bridge/parity")
]) ])
def test_existing_folders(host, path): def test_existing_folders(host, path):
@ -28,8 +27,7 @@ def test_existing_folders(host, path):
("/home/poadocker/bridge/commons/package.json"), ("/home/poadocker/bridge/commons/package.json"),
("/home/poadocker/bridge/contracts/package.json"), ("/home/poadocker/bridge/contracts/package.json"),
("/home/poadocker/bridge/oracle/package.json"), ("/home/poadocker/bridge/oracle/package.json"),
("/home/poadocker/bridge/monitor/package.json"), ("/home/poadocker/bridge/monitor/package.json")
("/home/poadocker/bridge/ui/package.json")
]) ])
def test_existing_package_json(host, path): def test_existing_package_json(host, path):
assert host.file(path).exists assert host.file(path).exists
@ -41,8 +39,7 @@ def test_existing_package_json(host, path):
("/home/poadocker/bridge/contracts/Dockerfile"), ("/home/poadocker/bridge/contracts/Dockerfile"),
("/home/poadocker/bridge/parity/Dockerfile"), ("/home/poadocker/bridge/parity/Dockerfile"),
("/home/poadocker/bridge/oracle/Dockerfile"), ("/home/poadocker/bridge/oracle/Dockerfile"),
("/home/poadocker/bridge/monitor/Dockerfile"), ("/home/poadocker/bridge/monitor/Dockerfile")
("/home/poadocker/bridge/ui/Dockerfile")
]) ])
def test_existing_docker_files(host, path): def test_existing_docker_files(host, path):
assert host.file(path).exists assert host.file(path).exists

@ -1,14 +0,0 @@
# Molecule managed
{% if item.registry is defined %}
FROM {{ item.registry.url }}/{{ item.image }}
{% else %}
FROM {{ item.image }}
{% endif %}
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi

@ -1,55 +0,0 @@
---
dependency:
name: galaxy
driver:
name: docker
lint:
name: yamllint
enabled: True
options:
config-data:
ignore: ../../hosts.yml
platforms:
- name: ui-host
groups:
- example
children:
- ui
image: ubuntu:16.04
privileged: true
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
provisioner:
name: ansible
lint:
name: ansible-lint
enabled: True
options:
r: ["bug"]
playbooks:
prepare: ../prepare.yml
converge: ../../../deployment/site.yml
inventory:
host_vars:
ui-host:
syslog_server_port: "udp://127.0.0.1:514"
verifier:
name: testinfra
lint:
name: flake8
additional_files_or_dirs:
- ../../tests/*
scenario:
name: ui
test_sequence:
- lint
- cleanup
- destroy
- dependency
- syntax
- create
- prepare
- converge
- verify
- destroy

@ -1,48 +0,0 @@
import os
import pytest
import testinfra.utils.ansible_runner
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('ui')
@pytest.mark.parametrize("name", [
("ui_ui_1")
])
def test_docker_containers(host, name):
container = host.docker(name)
assert container.is_running
@pytest.mark.parametrize("service", [
("tokenbridge-ui"),
("rsyslog")
])
def test_services(host, service):
assert host.service(service).is_enabled
assert host.service(service).is_running
@pytest.mark.parametrize("filename", [
("/etc/rsyslog.d/32-ui-docker.conf"),
("/etc/rsyslog.d/37-ui-remote-logging.conf")
])
def test_logging(host, filename):
assert host.file(filename).exists
assert host.file(filename).mode == 0o0644
def test_index_page_title(host):
assert host.run_test(
'curl -s http://localhost:3001 | '
'grep "<title>" | '
'grep -q "TokenBridge UI app"'
)
def test_index_page_error(host):
assert host.run_expect(
[1],
'curl -s http://localhost:3001 | '
'grep -i -q "error"'
)

@ -1,14 +0,0 @@
# Molecule managed
{% if item.registry is defined %}
FROM {{ item.registry.url }}/{{ item.image }}
{% else %}
FROM {{ item.image }}
{% endif %}
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi

@ -1,52 +0,0 @@
---
driver:
name: docker
platforms:
- name: oracle-amb-host
groups:
- ultimate
- amb
children:
- oracle
image: ubuntu:16.04
privileged: true
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- name: ui-amb-stake-erc-to-erc-host
groups:
- ultimate
- amb-stake-erc-to-erc
children:
- ui
image: ubuntu:16.04
privileged: true
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
provisioner:
name: ansible
playbooks:
prepare: ../prepare.yml
converge: ../ultimate-commons/converge.yml
inventory:
host_vars:
oracle-amb-host:
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
ui-amb-stake-erc-to-erc-host:
COMMON_HOME_RPC_URL: "http://localhost:8541"
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
verifier:
name: testinfra
lint:
name: flake8
scenario:
name: ultimate-amb-stake-erc-to-erc
test_sequence:
- cleanup
- destroy
- syntax
- create
- prepare
- converge

@ -18,5 +18,6 @@
- oracle_net_rabbit_bridge_transfer - oracle_net_rabbit_bridge_transfer
- oracle_net_rabbit_bridge_senderhome - oracle_net_rabbit_bridge_senderhome
- oracle_net_rabbit_bridge_senderforeign - oracle_net_rabbit_bridge_senderforeign
- oracle_net_rabbit_bridge_convert_to_chai_worker
delegate_to: 127.0.0.1 delegate_to: 127.0.0.1
become: false become: false

@ -13,17 +13,6 @@ platforms:
network_mode: host network_mode: host
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- name: ui-erc-to-erc-host
groups:
- ultimate
- erc-to-erc
children:
- ui
image: ubuntu:16.04
privileged: true
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
provisioner: provisioner:
name: ansible name: ansible
playbooks: playbooks:
@ -34,9 +23,6 @@ provisioner:
oracle-erc-to-erc-host: oracle-erc-to-erc-host:
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
ui-erc-to-erc-host:
COMMON_HOME_RPC_URL: "http://localhost:8541"
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
verifier: verifier:
name: testinfra name: testinfra
lint: lint:

@ -13,17 +13,6 @@ platforms:
network_mode: host network_mode: host
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- name: ui-erc-to-native-host
groups:
- ultimate
- erc-to-native
children:
- ui
image: ubuntu:16.04
privileged: true
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
provisioner: provisioner:
name: ansible name: ansible
playbooks: playbooks:
@ -36,9 +25,6 @@ provisioner:
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
ORACLE_HOME_START_BLOCK: 1 ORACLE_HOME_START_BLOCK: 1
ORACLE_FOREIGN_START_BLOCK: 1 ORACLE_FOREIGN_START_BLOCK: 1
ui-erc-to-native-host:
COMMON_HOME_RPC_URL: "http://localhost:8541"
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
verifier: verifier:
name: testinfra name: testinfra
lint: lint:

@ -13,17 +13,6 @@ platforms:
network_mode: host network_mode: host
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
- name: ui-native-to-erc-host
groups:
- ultimate
- native-to-erc
children:
- ui
image: ubuntu:16.04
privileged: true
network_mode: host
volumes:
- /var/run/docker.sock:/var/run/docker.sock
provisioner: provisioner:
name: ansible name: ansible
playbooks: playbooks:
@ -34,9 +23,6 @@ provisioner:
oracle-native-to-erc-host: oracle-native-to-erc-host:
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
ui-native-to-erc-host:
COMMON_HOME_RPC_URL: "http://localhost:8541"
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
verifier: verifier:
name: testinfra name: testinfra
lint: lint:

@ -34,14 +34,6 @@ cp hosts.yml.example hosts.yml
#syslog_server_port: "<protocol>://<ip>:<port>" # When this parameter is set all bridge logs will be redirected to <ip>:<port> address. #syslog_server_port: "<protocol>://<ip>:<port>" # When this parameter is set all bridge logs will be redirected to <ip>:<port> address.
<host_ip_B>: <host_ip_B>:
# (...) # (...)
ui:
hosts:
<host_ip_B>:
ansible_user: <user>
#syslog_server_port: "<protocol>://<ip>:<port>"
<host_ip_C>:
ansible_user: <user>
#syslog_server_port: "<protocol>://<ip>:<port>"
monitor: monitor:
hosts: hosts:
<host_ip_B>: <host_ip_B>:
@ -50,18 +42,7 @@ cp hosts.yml.example hosts.yml
#monitor_cron_schedule: "*/4 * * * *" # When this parameter is set, it will overwrite default schedule for performing checks #monitor_cron_schedule: "*/4 * * * *" # When this parameter is set, it will overwrite default schedule for performing checks
``` ```
The config above would install the Oracle on `<host_ip_A>`, UI on `<host_ip_C>`, and both Oracle, UI and Monitor on `<host_ip_B>`. The config above would install the Oracle on `<host_ip_A>`, and both Oracle and Monitor on `<host_ip_B>`.
Example config for installing only UI:
```yaml
<bridge_name>:
children:
oracle:
hosts:
ui:
hosts:
<host_ip>:
ansible_user: <user>
``` ```
| Value | Description | | Value | Description |

@ -44,7 +44,6 @@ The deployed components have the following services:
Component | Service Name Component | Service Name
--- | --- --- | ---
Oracle | poabridge Oracle | poabridge
UI | tokenbridge-ui
Monitor | tokenbridge-monitor Monitor | tokenbridge-monitor
Use the default `SysVinit` commands to `start`, `stop`, `restart`, and `rebuild` the service and to check the `status` of the service. Use the default `SysVinit` commands to `start`, `stop`, `restart`, and `rebuild` the service and to check the `status` of the service.

@ -1,4 +0,0 @@
---
COMMON_HOME_BRIDGE_ADDRESS: "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC"
COMMON_FOREIGN_BRIDGE_ADDRESS: "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC"
UI_PORT: 3003

@ -1,19 +1,14 @@
--- ---
## General settings ## General settings
ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE" ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE"
UI_NATIVE_TOKEN_DISPLAY_NAME: "xDai"
## Home contract ## Home contract
COMMON_HOME_RPC_URL: "https://dai.poa.network" COMMON_HOME_RPC_URL: "https://dai.poa.network"
UI_HOME_NETWORK_DISPLAY_NAME: "xDai chain"
UI_HOME_WITHOUT_EVENTS: false
COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6" COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000 ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
## Foreign contract ## Foreign contract
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io" COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io"
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Ethereum Mainnet"
UI_FOREIGN_WITHOUT_EVENTS: false
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016" COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016"
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 5000 ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 5000
@ -30,20 +25,6 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000 ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1 COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
## UI
UI_TITLE: "TokenBridge UI app - %c"
UI_OG_TITLE: "POA Bridge UI"
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring MakerDAO stable tokens between the Ethereum network to xDai chain in a quick and cost-efficient manner."
UI_PORT: 3001
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/dai/tx/%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/mainnet/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/dai/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_STYLES: "core"
UI_PUBLIC_URL: "https://dai-bridge.poa.network"
## Monitor ## Monitor
MONITOR_BRIDGE_NAME: "xdai" MONITOR_BRIDGE_NAME: "xdai"
MONITOR_PORT: 3003 MONITOR_PORT: 3003

@ -2,5 +2,4 @@
ORACLE_BRIDGE_MODE: "ERC_TO_ERC" ORACLE_BRIDGE_MODE: "ERC_TO_ERC"
COMMON_HOME_BRIDGE_ADDRESS: "0x1feB40aD9420b186F019A717c37f5546165d411E" COMMON_HOME_BRIDGE_ADDRESS: "0x1feB40aD9420b186F019A717c37f5546165d411E"
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127" COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127"
UI_PORT: 3001
MONITOR_PORT: 3011 MONITOR_PORT: 3011

@ -2,5 +2,4 @@
ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE" ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE"
COMMON_HOME_BRIDGE_ADDRESS: "0x488Af810997eD1730cB3a3918cD83b3216E6eAda" COMMON_HOME_BRIDGE_ADDRESS: "0x488Af810997eD1730cB3a3918cD83b3216E6eAda"
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x488Af810997eD1730cB3a3918cD83b3216E6eAda" COMMON_FOREIGN_BRIDGE_ADDRESS: "0x488Af810997eD1730cB3a3918cD83b3216E6eAda"
UI_PORT: 3002
MONITOR_PORT: 3012 MONITOR_PORT: 3012

@ -2,19 +2,14 @@
## General settings ## General settings
ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC" ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC"
ORACLE_LOG_LEVEL: debug ORACLE_LOG_LEVEL: debug
UI_NATIVE_TOKEN_DISPLAY_NAME: "POA"
## Home contract ## Home contract
COMMON_HOME_RPC_URL: "https://sokol.poa.network" COMMON_HOME_RPC_URL: "https://sokol.poa.network"
UI_HOME_NETWORK_DISPLAY_NAME: "POA Sokol"
UI_HOME_WITHOUT_EVENTS: false
COMMON_HOME_BRIDGE_ADDRESS: "0x98aFdE294f1C46aA0a27Cc4049ED337F879d8976" COMMON_HOME_BRIDGE_ADDRESS: "0x98aFdE294f1C46aA0a27Cc4049ED337F879d8976"
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000 ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
## Foreign contract ## Foreign contract
COMMON_FOREIGN_RPC_URL: "https://sokol.poa.network" COMMON_FOREIGN_RPC_URL: "https://sokol.poa.network"
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Kovan"
UI_FOREIGN_WITHOUT_EVENTS: false
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x5a584f4C30B36f282848dAc9a2b20E7BEF481981" COMMON_FOREIGN_BRIDGE_ADDRESS: "0x5a584f4C30B36f282848dAc9a2b20E7BEF481981"
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000 ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000
@ -32,20 +27,6 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK: 1000000000 # in wei
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1 COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000 ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
## UI
UI_TITLE: "TokenBridge UI app - %c"
UI_OG_TITLE: "POA Bridge UI"
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
UI_PORT: 3001
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/kovan/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_STYLES: "core"
UI_PUBLIC_URL: "http://localhost:3001"
## Monitor ## Monitor
MONITOR_BRIDGE_NAME: "bridge" MONITOR_BRIDGE_NAME: "bridge"
MONITOR_PORT: 3003 MONITOR_PORT: 3003

@ -2,5 +2,4 @@
ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC" ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC"
COMMON_HOME_BRIDGE_ADDRESS: "0x32198D570fffC7033641F8A9094FFDCaAEF42624" COMMON_HOME_BRIDGE_ADDRESS: "0x32198D570fffC7033641F8A9094FFDCaAEF42624"
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x2B6871b9B02F73fa24F4864322CdC78604207769" COMMON_FOREIGN_BRIDGE_ADDRESS: "0x2B6871b9B02F73fa24F4864322CdC78604207769"
UI_PORT: 3000
MONITOR_PORT: 3010 MONITOR_PORT: 3010

@ -1,19 +1,14 @@
--- ---
## General settings ## General settings
UI_NATIVE_TOKEN_DISPLAY_NAME: "POA"
ORACLE_ALLOW_HTTP_FOR_RPC: yes ORACLE_ALLOW_HTTP_FOR_RPC: yes
ORACLE_LOG_LEVEL: debug ORACLE_LOG_LEVEL: debug
## Home contract ## Home contract
COMMON_HOME_RPC_URL: "http://parity1:8545" COMMON_HOME_RPC_URL: "http://parity1:8545"
UI_HOME_NETWORK_DISPLAY_NAME: "POA Sokol"
UI_HOME_WITHOUT_EVENTS: false
ORACLE_HOME_RPC_POLLING_INTERVAL: 5000 ORACLE_HOME_RPC_POLLING_INTERVAL: 5000
## Foreign contract ## Foreign contract
COMMON_FOREIGN_RPC_URL: "http://parity2:8545" COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Kovan"
UI_FOREIGN_WITHOUT_EVENTS: false
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000 ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000
## Home Gasprice ## Home Gasprice
@ -30,20 +25,6 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK: 1000000000 # in wei
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1 COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000 ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
#ui
UI_TITLE: "TokenBridge UI app - %c"
UI_OG_TITLE: "POA Bridge UI"
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/kovan/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_STYLES: "core"
UI_PUBLIC_URL: "http://localhost"
#monitor #monitor
MONITOR_BRIDGE_NAME: "bridge" MONITOR_BRIDGE_NAME: "bridge"
MONITOR_CACHE_EVENTS: "true" MONITOR_CACHE_EVENTS: "true"

@ -1,19 +1,14 @@
--- ---
## General settings ## General settings
ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC" ORACLE_BRIDGE_MODE: "NATIVE_TO_ERC"
UI_NATIVE_TOKEN_DISPLAY_NAME: "ETC"
## Home contract ## Home contract
COMMON_HOME_RPC_URL: "https://www.ethercluster.com/etc" COMMON_HOME_RPC_URL: "https://www.ethercluster.com/etc"
UI_HOME_NETWORK_DISPLAY_NAME: "Ethereum Classic"
UI_HOME_WITHOUT_EVENTS: false
COMMON_HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9" COMMON_HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9"
ORACLE_HOME_RPC_POLLING_INTERVAL: 7000 ORACLE_HOME_RPC_POLLING_INTERVAL: 7000
## Foreign contract ## Foreign contract
COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io/" COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io/"
UI_FOREIGN_NETWORK_DISPLAY_NAME: "Ethereum Mainnet"
UI_FOREIGN_WITHOUT_EVENTS: false
COMMON_FOREIGN_BRIDGE_ADDRESS: "0x0cB781EE62F815bdD9CD4c2210aE8600d43e7040" COMMON_FOREIGN_BRIDGE_ADDRESS: "0x0cB781EE62F815bdD9CD4c2210aE8600d43e7040"
ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 7000 ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 7000
@ -31,20 +26,6 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000 # in wei
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000 ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
COMMON_FOREIGN_GAS_PRICE_FACTOR: 1 COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
## UI
UI_TITLE: "TokenBridge UI app - %c"
UI_OG_TITLE: "POA Bridge UI"
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring native tokens from the Ethereum Classic Network to the Ethereum network in a quick and cost-efficient manner."
UI_PORT: 3001
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/etc/mainnet/tx/%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/mainnet/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/etc/mainnet/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
UI_STYLES: "classic"
UI_PUBLIC_URL: "https://wetc.app"
## Monitor ## Monitor
MONITOR_BRIDGE_NAME: "wetc" MONITOR_BRIDGE_NAME: "wetc"
MONITOR_PORT: 3003 MONITOR_PORT: 3003

@ -7,11 +7,6 @@ sokol-kovan:
ansible_user: ubuntu ansible_user: ubuntu
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
#syslog_server_port: "udp://127.0.0.1:514" #syslog_server_port: "udp://127.0.0.1:514"
ui:
hosts:
127.0.0.1:
ansible_user: ubuntu
#syslog_server_port: "udp://127.0.0.1:514"
monitor: monitor:
hosts: hosts:
127.0.0.1: 127.0.0.1:

@ -1,3 +0,0 @@
---
dependencies:
- role: common

@ -1,6 +0,0 @@
---
- name: Build the containers
shell: docker-compose build
args:
chdir: "{{ bridge_path }}/ui"
when: skip_build is undefined

@ -1,41 +0,0 @@
---
- name: Slurp docker compose file
slurp:
src: "{{ bridge_path }}/ui/docker-compose.yml"
register: docker_compose_slurp
- name: Parse docker compose file
set_fact:
docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}"
- name: Set logger to remote server
set_fact:
docker_compose_parsed: "{{ docker_compose_parsed |combine({'services': {item: {'logging': {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}}}}}, recursive=True) }}"
with_items: "{{ docker_compose_parsed.services }}"
- name: Write new docker-compose file
copy:
content: "{{ docker_compose_parsed | to_yaml }}"
dest: "{{ bridge_path }}/ui/docker-compose.yml"
- name: Set the local container logs configuration file
template:
src: 32-ui-docker.conf.j2
dest: /etc/rsyslog.d/32-ui-docker.conf
owner: root
group: root
mode: 0644
- name: Set the log configuration file to send container logs to remote server
template:
src: 37-ui-remote-logging.conf.j2
dest: /etc/rsyslog.d/37-ui-remote-logging.conf
owner: root
group: root
mode: 0644
when: syslog_server_port is defined
- name: restart rsyslog
service:
name: rsyslog
state: restarted

@ -1,5 +0,0 @@
---
- include_tasks: pre_config.yml
- include_tasks: logging.yml
- include_tasks: jumpbox.yml
- include_tasks: servinstall.yml

@ -1,7 +0,0 @@
---
- name: Install .env config
template:
src: .env.j2
dest: "{{ bridge_path }}/ui/.env"
owner: "{{ compose_service_user }}"
mode: '0640'

@ -1,20 +0,0 @@
# This role creates a tokenbridge-ui service which is designed to manage docker-compose ui deployment.
# /etc/init.d/tokenbridge-ui start, status, stop, restart - does what the services usually do in such cases.
# /etc/init.d/tokenbridge-ui rebuild - builds a new ui deployment from scratch.
---
- name: "Set the service"
template:
src: tokenbridge-ui.j2
dest: "/etc/init.d/tokenbridge-ui"
owner: root
mode: 755
- name: "Enable the service"
service:
name: "tokenbridge-ui"
state: started
enabled: yes
use: service
- name: Start the service
shell: service tokenbridge-ui start

@ -1,42 +0,0 @@
COMMON_HOME_BRIDGE_ADDRESS={{ COMMON_HOME_BRIDGE_ADDRESS }}
COMMON_FOREIGN_BRIDGE_ADDRESS={{ COMMON_FOREIGN_BRIDGE_ADDRESS }}
COMMON_FOREIGN_RPC_URL={{ COMMON_FOREIGN_RPC_URL }}
COMMON_HOME_RPC_URL={{ COMMON_HOME_RPC_URL }}
UI_NATIVE_TOKEN_DISPLAY_NAME={{ UI_NATIVE_TOKEN_DISPLAY_NAME }}
UI_HOME_NETWORK_DISPLAY_NAME={{ UI_HOME_NETWORK_DISPLAY_NAME }}
UI_FOREIGN_NETWORK_DISPLAY_NAME={{ UI_FOREIGN_NETWORK_DISPLAY_NAME }}
UI_HOME_WITHOUT_EVENTS={{ UI_HOME_WITHOUT_EVENTS }}
UI_FOREIGN_WITHOUT_EVENTS={{ UI_FOREIGN_WITHOUT_EVENTS }}
UI_HOME_EXPLORER_TX_TEMPLATE={{ UI_HOME_EXPLORER_TX_TEMPLATE }}
UI_FOREIGN_EXPLORER_TX_TEMPLATE={{ UI_FOREIGN_EXPLORER_TX_TEMPLATE }}
UI_HOME_EXPLORER_ADDRESS_TEMPLATE={{ UI_HOME_EXPLORER_ADDRESS_TEMPLATE }}
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE={{ UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE }}
{% if COMMON_HOME_GAS_PRICE_SUPPLIER_URL | default('') != '' %}
COMMON_HOME_GAS_PRICE_SUPPLIER_URL={{ COMMON_HOME_GAS_PRICE_SUPPLIER_URL }}
COMMON_HOME_GAS_PRICE_SPEED_TYPE={{ COMMON_HOME_GAS_PRICE_SPEED_TYPE }}
{% endif %}
COMMON_HOME_GAS_PRICE_FALLBACK={{ COMMON_HOME_GAS_PRICE_FALLBACK }}
UI_HOME_GAS_PRICE_UPDATE_INTERVAL={{ UI_HOME_GAS_PRICE_UPDATE_INTERVAL }}
{% if COMMON_HOME_GAS_PRICE_FACTOR | default('') != '' %}
COMMON_HOME_GAS_PRICE_FACTOR={{ COMMON_HOME_GAS_PRICE_FACTOR }}
{% endif %}
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL={{ COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL }}
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE={{ COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE }}
COMMON_FOREIGN_GAS_PRICE_FALLBACK={{ COMMON_FOREIGN_GAS_PRICE_FALLBACK }}
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL={{ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL }}
COMMON_FOREIGN_GAS_PRICE_FACTOR={{ COMMON_FOREIGN_GAS_PRICE_FACTOR }}
# Default
UI_TITLE={{ UI_TITLE }}
UI_OG_TITLE={{ UI_OG_TITLE }}
UI_DESCRIPTION={{ UI_DESCRIPTION }}
UI_PORT={{ UI_PORT }}
UI_PUBLIC_URL={{ UI_PUBLIC_URL }}
UI_STYLES={{ UI_STYLES }}

@ -1,11 +0,0 @@
$FileCreateMode 0644
template(name="DockerLogFileName_UI" type="list") {
constant(value="/var/log/docker/")
property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="ui_(.*)\\/[a-zA-Z0-9]+\\[")
constant(value="/docker.log")
}
if $programname startswith 'ui_' then \
?DockerLogFileName_UI
$FileCreateMode 0600

@ -1,15 +0,0 @@
if $programname startswith 'ui_' then {
action(
type="omfwd"
protocol="{{ syslog_server_port.split(":")[0] }}"
target="{{ (syslog_server_port.split(":")[1])[2:] }}"
port="{{ syslog_server_port.split(":")[2] }}"
template="RemoteForwardFormat"
queue.SpoolDirectory="/var/spool/rsyslog"
queue.FileName="remote"
queue.MaxDiskSpace="1g"
queue.SaveOnShutdown="on"
queue.Type="LinkedList"
ResendLastMSGOnReconnect="on"
)
}

@ -1,76 +0,0 @@
#! /bin/bash
### BEGIN INIT INFO
# Provides: tokenbridge-ui
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start daemon at boot time
# Description: Enable service provided by daemon.
### END INIT INFO
WORKDIR="{{ '/home/' + compose_service_user | default('poadocker') + '/' + bridge_path + '/ui' if bridge_path[:1] != "/" else bridge_path + '/ui' }}"
start(){
echo "Starting TokenBridge UI.."
cd $WORKDIR
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose up --detach
}
stop(){
echo "Stopping TokenBridge UI.."
cd $WORKDIR
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
sleep 2
}
status(){
echo "TokenBridge UI status:"
cd $WORKDIR
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose ps
}
rebuild(){
echo "Rebuild TokenBridge UI.."
cd $WORKDIR
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv
sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose up --detach --force-recreate --no-deps --build
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
echo "Restarting TokenBridge UI.."
stop
start
;;
rebuild)
rebuild
;;
*)
echo $"Usage: $0 {start|stop|restart|rebuild|status}"
exit 1
;;
esac
exit 0

@ -4,11 +4,6 @@
become: true become: true
roles: roles:
- { role: oracle } - { role: oracle }
- name: Install UI
hosts: ui
become: true
roles:
- { role: ui }
- name: Install Monitor - name: Install Monitor
hosts: monitor hosts: monitor
become: true become: true

@ -1,7 +0,0 @@
ARG DOCKER_IMAGE_BASE
ARG UI_TAG
FROM ${DOCKER_IMAGE_BASE}/tokenbridge-e2e-ui:${UI_TAG}
ARG DOT_ENV_PATH
COPY ${DOT_ENV_PATH} ./.env

@ -24,14 +24,12 @@ Shut down and cleans up containers, networks, services, running scripts:
| oracle | Launches Oracle containers | | oracle | Launches Oracle containers |
| oracle-validator-2 | Launches Oracle containers for second validator | | oracle-validator-2 | Launches Oracle containers for second validator |
| oracle-validator-3 | Launches Oracle containers for third validator | | oracle-validator-3 | Launches Oracle containers for third validator |
| ui | Launches UI containers |
| blocks | Auto mines blocks | | blocks | Auto mines blocks |
| monitor | Launches Monitor containers | | monitor | Launches Monitor containers |
| native-to-erc | Creates infrastructure for ultimate e2e testing, for native-to-erc type of bridge | | native-to-erc | Creates infrastructure for ultimate e2e testing, for native-to-erc type of bridge |
| erc-to-native | Creates infrastructure for ultimate e2e testing, for erc-to-native type of bridge | | erc-to-native | Creates infrastructure for ultimate e2e testing, for erc-to-native type of bridge |
| erc-to-erc | Creates infrastructure for ultimate e2e testing, for erc-to-erc type of bridge | | erc-to-erc | Creates infrastructure for ultimate e2e testing, for erc-to-erc type of bridge |
| amb | Creates infrastructure for ultimate e2e testing, for arbitrary message type of bridge | | amb | Creates infrastructure for ultimate e2e testing, for arbitrary message type of bridge |
| ultimate-amb-stake-erc-to-erc | Creates infrastructure for ultimate e2e testing, for stake token bridge |
#### Ultimate e2e testing #### Ultimate e2e testing

@ -15,13 +15,15 @@ It runs the e2e tests on components deployed using the deployment playbooks.
Run the Parity nodes, deploy the bridge contracts, deploy Oracle using the deployment playbook. Run the Parity nodes, deploy the bridge contracts, deploy Oracle using the deployment playbook.
```bash ```bash
./up.sh deploy native-to-erc blocks ./e2e-commons/up.sh deploy blocks
./deployment-e2e/molecule.sh ultimate-native-to-erc
``` ```
### 2. Run the E2E tests ### 2. Run the E2E tests
``` ```bash
cd ui-e2e; yarn mocha -g "NATIVE_TO_ERC" -b ./test.js cd e2e-commons
docker-compose run -e ULTIMATE=true e2e yarn workspace oracle-e2e run native-to-erc
``` ```
## Diagram ## Diagram

@ -1,2 +1,2 @@
0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04 0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04
0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE 0x6f359aC418a5f7538F7755A33C9c7dDf38785524

@ -8,8 +8,6 @@ while [ "$1" != "" ]; do
docker-compose build oracle docker-compose build oracle
elif [ "$1" == "monitor" ]; then elif [ "$1" == "monitor" ]; then
docker-compose build monitor docker-compose build monitor
elif [ "$1" == "ui" ]; then
docker-compose build ui
elif [ "$1" == "alm" ]; then elif [ "$1" == "alm" ]; then
docker-compose build alm docker-compose build alm
fi fi

@ -1,23 +0,0 @@
COMMON_HOME_BRIDGE_ADDRESS=0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC
COMMON_FOREIGN_BRIDGE_ADDRESS=0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC
COMMON_FOREIGN_RPC_URL=http://localhost:8542
COMMON_HOME_RPC_URL=http://localhost:8541
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
UI_HOME_NETWORK_DISPLAY_NAME=Sokol
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_HOME_GAS_PRICE_FACTOR=1
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3003
UI_STYLES=stake

@ -1,23 +0,0 @@
COMMON_HOME_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda
COMMON_FOREIGN_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda
COMMON_FOREIGN_RPC_URL=http://localhost:8542
COMMON_HOME_RPC_URL=http://localhost:8541
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
UI_HOME_NETWORK_DISPLAY_NAME=Sokol
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_HOME_GAS_PRICE_FACTOR=1
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3002
UI_STYLES=core

@ -1,23 +0,0 @@
COMMON_HOME_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E
COMMON_FOREIGN_BRIDGE_ADDRESS=0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127
COMMON_FOREIGN_RPC_URL=http://localhost:8542
COMMON_HOME_RPC_URL=http://localhost:8541
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
UI_HOME_NETWORK_DISPLAY_NAME=Sokol
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_HOME_GAS_PRICE_FACTOR=1
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3001
UI_STYLES=core

@ -1,23 +0,0 @@
COMMON_HOME_BRIDGE_ADDRESS=0x32198D570fffC7033641F8A9094FFDCaAEF42624
COMMON_FOREIGN_BRIDGE_ADDRESS=0x2B6871b9B02F73fa24F4864322CdC78604207769
COMMON_FOREIGN_RPC_URL=http://localhost:8542
COMMON_HOME_RPC_URL=http://localhost:8541
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
UI_HOME_NETWORK_DISPLAY_NAME=Sokol
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_HOME_GAS_PRICE_FACTOR=1
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
UI_PORT=3000
UI_STYLES=core

@ -39,7 +39,6 @@
"home": "0x32198D570fffC7033641F8A9094FFDCaAEF42624", "home": "0x32198D570fffC7033641F8A9094FFDCaAEF42624",
"foreign": "0x2B6871b9B02F73fa24F4864322CdC78604207769", "foreign": "0x2B6871b9B02F73fa24F4864322CdC78604207769",
"foreignToken": "0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B", "foreignToken": "0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B",
"ui": "http://localhost:3000",
"monitor": "http://monitor:3010/bridge" "monitor": "http://monitor:3010/bridge"
}, },
"ercToErcBridge": { "ercToErcBridge": {
@ -47,7 +46,6 @@
"foreign": "0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127", "foreign": "0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127",
"homeToken": "0x792455a6bCb62Ed4C4362D323E0590654CA4765c", "homeToken": "0x792455a6bCb62Ed4C4362D323E0590654CA4765c",
"foreignToken": "0x3C665A31199694Bf723fD08844AD290207B5797f", "foreignToken": "0x3C665A31199694Bf723fD08844AD290207B5797f",
"ui": "http://localhost:3001",
"monitor": "http://monitor-erc20:3011/bridge" "monitor": "http://monitor-erc20:3011/bridge"
}, },
"ercToNativeBridge": { "ercToNativeBridge": {
@ -55,7 +53,6 @@
"foreign": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda", "foreign": "0x488Af810997eD1730cB3a3918cD83b3216E6eAda",
"foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9", "foreignToken": "0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9",
"chaiToken": "0x06af07097c9eeb7fd685c692751d5c66db49c215", "chaiToken": "0x06af07097c9eeb7fd685c692751d5c66db49c215",
"ui": "http://localhost:3002",
"monitor": "http://monitor-erc20-native:3012/bridge" "monitor": "http://monitor-erc20-native:3012/bridge"
}, },
"amb": { "amb": {
@ -63,17 +60,9 @@
"foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0", "foreign": "0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0",
"homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1", "homeBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
"foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1", "foreignBox": "0x6C4EaAb8756d53Bf599FFe2347FAFF1123D6C8A1",
"blockedHomeBox": "0x612E8bd50A7b1F009F43f2b8679E9B8eD91eb5CE", "blockedHomeBox": "0x6f359aC418a5f7538F7755A33C9c7dDf38785524",
"monitor": "http://monitor-amb:3013/bridge" "monitor": "http://monitor-amb:3013/bridge"
}, },
"ambStakeErcToErc": {
"home": "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC",
"foreign": "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC",
"homeToken": "0x6f359aC418a5f7538F7755A33C9c7dDf38785524",
"foreignToken": "0x6f359aC418a5f7538F7755A33C9c7dDf38785524",
"blockReward": "0xF9698Eb93702dfdd0e2d802088d4c21822a8A977",
"ui": "http://localhost:3003"
},
"homeRPC": { "homeRPC": {
"URL": "http://parity1:8545", "URL": "http://parity1:8545",
"ID": "77" "ID": "77"

@ -1,30 +0,0 @@
BRIDGE_MODE=STAKE_AMB_ERC_TO_ERC
DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
DEPLOYMENT_GAS_LIMIT_EXTRA=0.2
HOME_DEPLOYMENT_GAS_PRICE=10000000000
FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000
GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50
HOME_STAKE_TOKEN_ADDRESS=0x6f359aC418a5f7538F7755A33C9c7dDf38785524
FOREIGN_STAKE_TOKEN_ADDRESS=0x6f359aC418a5f7538F7755A33C9c7dDf38785524
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
HOME_RPC_URL=http://parity1:8545
HOME_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
HOME_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
HOME_DAILY_LIMIT=30000000000000000000000000
HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000
HOME_MIN_AMOUNT_PER_TX=10000000000000000
HOME_TRANSACTIONS_FEE=0
FOREIGN_RPC_URL=http://parity2:8545
FOREIGN_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
FOREIGN_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
FOREIGN_DAILY_LIMIT=15000000000000000000000000
FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000
FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000
HOME_AMB_BRIDGE=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
FOREIGN_AMB_BRIDGE=0x0AEe1FCD12dDFab6265F7f8956e6E012A9Fe4Aa0
HOME_MEDIATOR_REQUEST_GAS_LIMIT=2000000
FOREIGN_MEDIATOR_REQUEST_GAS_LIMIT=2000000
BRIDGEABLE_TOKEN_NAME='not used'
BRIDGEABLE_TOKEN_SYMBOL='not used'
BRIDGEABLE_TOKEN_DECIMALS='18'

@ -68,49 +68,6 @@ services:
- '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt' - '../e2e-commons/access-lists/allowance_list.txt:/mono/oracle/access-lists/allowance_list.txt'
networks: networks:
- ultimate - ultimate
ui:
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-ui:${UI_TAG:-local}
build:
context: ..
dockerfile: ui/Dockerfile
args:
DOT_ENV_PATH: e2e-commons/components-envs/ui.env
command: "true"
networks:
- ultimate
ui-erc20:
build:
context: ..
dockerfile: e2e-commons/Dockerfile.ui
args:
DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
UI_TAG: ${UI_TAG:-local}
DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20.env
command: "true"
networks:
- ultimate
ui-erc20-native:
build:
context: ..
dockerfile: e2e-commons/Dockerfile.ui
args:
DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
UI_TAG: ${UI_TAG:-local}
DOT_ENV_PATH: e2e-commons/components-envs/ui-erc20-native.env
command: "true"
networks:
- ultimate
ui-amb-stake-erc20-erc20:
build:
context: ..
dockerfile: e2e-commons/Dockerfile.ui
args:
DOCKER_IMAGE_BASE: ${DOCKER_IMAGE_BASE:-tokenbridge}
UI_TAG: ${UI_TAG:-local}
DOT_ENV_PATH: e2e-commons/components-envs/ui-amb-stake-erc20-erc20.env
command: "true"
networks:
- ultimate
alm: alm:
image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-alm:${ALM_TAG:-local} image: ${DOCKER_IMAGE_BASE:-tokenbridge}/tokenbridge-e2e-alm:${ALM_TAG:-local}
build: build:

@ -3,7 +3,7 @@ cd $(dirname $0)
if [ $CI ]; then exit $rc; fi if [ $CI ]; then exit $rc; fi
ps | grep node | grep -v grep | awk '{print "kill " $1}' | /bin/bash ps | grep node | grep -v grep | grep -v yarn | awk '{print "kill " $1}' | /bin/bash
docker-compose down docker-compose down
docker-compose -p validator2 down docker-compose -p validator2 down
docker-compose -p validator3 down docker-compose -p validator3 down

@ -8,8 +8,6 @@ while [ "$1" != "" ]; do
docker-compose pull oracle docker-compose pull oracle
elif [ "$1" == "monitor" ]; then elif [ "$1" == "monitor" ]; then
docker-compose pull monitor docker-compose pull monitor
elif [ "$1" == "ui" ]; then
docker-compose pull ui
elif [ "$1" == "alm" ]; then elif [ "$1" == "alm" ]; then
docker-compose pull alm docker-compose pull alm
fi fi

@ -44,16 +44,6 @@ cd "$DEPLOY_PATH"
node src/utils/deployTestBox.js node src/utils/deployTestBox.js
cd - > /dev/null cd - > /dev/null
echo -e "\n\n############ Deploying amb stake erc to erc ############\n"
cp "$ENVS_PATH/amb-stake-erc-to-erc.env" "$DEPLOY_PATH/.env"
node deployMultiBridgeToken.js
node deployBridgeTokenRewardable.js
cd "$DEPLOY_PATH"
node deploy.js
cd - > /dev/null
node setupStakeTokens.js
cd - > /dev/null
echo -e "\n\n############ Deploying one more test contract for amb ############\n" echo -e "\n\n############ Deploying one more test contract for amb ############\n"
cd "$DEPLOY_PATH" cd "$DEPLOY_PATH"
node src/utils/deployTestBox.js node src/utils/deployTestBox.js

@ -1,111 +0,0 @@
const path = require('path')
const { ambStakeErcToErc, validator, secondValidator, thirdValidator } = require('../constants.json')
const contractsPath = '../../contracts'
require('dotenv').config({
path: path.join(__dirname, contractsPath, '/deploy/.env')
})
const { sendRawTxHome, sendRawTxForeign, privateKeyToAddress } = require(`${contractsPath}/deploy/src/deploymentUtils`)
const { web3Home, web3Foreign, deploymentPrivateKey } = require(`${contractsPath}/deploy/src/web3`)
const BlockReward = require(`${contractsPath}/build/contracts/BlockRewardMock.json`)
const ERC677BridgeTokenRewardable = require(`${contractsPath}/build/contracts/ERC677BridgeTokenRewardable.json`)
const ERC677MultiBridgeToken = require(`${contractsPath}/build/contracts/ERC677MultiBridgeToken.json`)
const { DEPLOYMENT_ACCOUNT_PRIVATE_KEY } = process.env
const DEPLOYMENT_ACCOUNT_ADDRESS = privateKeyToAddress(DEPLOYMENT_ACCOUNT_PRIVATE_KEY)
async function setupStakeTokens() {
try {
let homeNonce = await web3Home.eth.getTransactionCount(DEPLOYMENT_ACCOUNT_ADDRESS)
const blockReward = new web3Home.eth.Contract(BlockReward.abi, ambStakeErcToErc.blockReward)
console.log('\n[Home] Set token in block reward')
const setTokenData = await blockReward.methods.setToken(ambStakeErcToErc.homeToken).encodeABI()
await sendRawTxHome({
data: setTokenData,
nonce: homeNonce,
to: blockReward.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
console.log('\n[Home] Set validators rewards in block reward')
const setValidatorsRewardsData = await blockReward.methods
.setValidatorsRewards([validator.address, secondValidator.address, thirdValidator.address])
.encodeABI()
await sendRawTxHome({
data: setValidatorsRewardsData,
nonce: homeNonce,
to: blockReward.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
const homeToken = new web3Home.eth.Contract(ERC677BridgeTokenRewardable.abi, ambStakeErcToErc.homeToken)
console.log('\n[Home] Set block reward in token')
const setBlockRewardData = await homeToken.methods.setBlockRewardContract(ambStakeErcToErc.blockReward).encodeABI()
await sendRawTxHome({
data: setBlockRewardData,
nonce: homeNonce,
to: homeToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
console.log('\n[Home] Add bridge in token')
const addBridgeData = await homeToken.methods.addBridge(ambStakeErcToErc.home).encodeABI()
await sendRawTxHome({
data: addBridgeData,
nonce: homeNonce,
to: homeToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
console.log('\n[Home] transfer token ownership to mediator')
const transferOwnershipData = await homeToken.methods.transferOwnership(ambStakeErcToErc.home).encodeABI()
await sendRawTxHome({
data: transferOwnershipData,
nonce: homeNonce,
to: homeToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.HOME_RPC_URL
})
homeNonce++
let foreignNonce = await web3Foreign.eth.getTransactionCount(DEPLOYMENT_ACCOUNT_ADDRESS)
const foreignToken = new web3Foreign.eth.Contract(ERC677MultiBridgeToken.abi, ambStakeErcToErc.foreignToken)
console.log('\n[Foreign] Add bridge in token')
const addBridgeForeignData = await homeToken.methods.addBridge(ambStakeErcToErc.foreign).encodeABI()
await sendRawTxForeign({
data: addBridgeForeignData,
nonce: foreignNonce,
to: foreignToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.FOREIGN_RPC_URL
})
foreignNonce++
console.log('\n[Foreign] transfer token ownership to mediator')
const transferOwnershipForeignData = await homeToken.methods.transferOwnership(ambStakeErcToErc.foreign).encodeABI()
await sendRawTxForeign({
data: transferOwnershipForeignData,
nonce: foreignNonce,
to: foreignToken.options.address,
privateKey: deploymentPrivateKey,
url: process.env.FOREIGN_RPC_URL
})
foreignNonce++
} catch (e) {
console.log(e)
throw e
}
}
setupStakeTokens()

@ -75,18 +75,6 @@ while [ "$1" != "" ]; do
startValidator "$oracle3name" "$oracle3Values" "$oracle3comp" "redis3" "rabbit3" startValidator "$oracle3name" "$oracle3Values" "$oracle3comp" "redis3" "rabbit3"
fi fi
if [ "$1" == "ui" ]; then
# this should only rebuild last 3 steps from ui/Dockerfile
docker-compose build ui-erc20 ui-erc20-native ui-amb-stake-erc20-erc20
docker-compose up -d ui ui-erc20 ui-erc20-native ui-amb-stake-erc20-erc20
docker-compose run -d -p 3000:3000 ui yarn start
docker-compose run -d -p 3001:3001 ui-erc20 yarn start
docker-compose run -d -p 3002:3002 ui-erc20-native yarn start
docker-compose run -d -p 3003:3003 ui-amb-stake-erc20-erc20 yarn start
fi
if [ "$1" == "alm" ]; then if [ "$1" == "alm" ]; then
docker-compose up -d alm docker-compose up -d alm

@ -1,5 +1,5 @@
require('dotenv').config() require('dotenv').config()
const logger = require('./logger')('alerts') const logger = require('./logger')('detectFailures.js')
const eventsInfo = require('./utils/events') const eventsInfo = require('./utils/events')
const { normalizeAMBMessageEvent } = require('../commons') const { normalizeAMBMessageEvent } = require('../commons')
const { getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3') const { getHomeBlockNumber, getForeignBlockNumber } = require('./utils/web3')

@ -1,5 +1,5 @@
require('dotenv').config() require('dotenv').config()
const logger = require('./logger')('stuckTransfers.js') const logger = require('./logger')('detectMediators.js')
const { isHomeContract, isForeignContract } = require('./utils/web3Cache') const { isHomeContract, isForeignContract } = require('./utils/web3Cache')
const eventsInfo = require('./utils/events') const eventsInfo = require('./utils/events')
const { getHomeTxSender, getForeignTxSender } = require('./utils/web3Cache') const { getHomeTxSender, getForeignTxSender } = require('./utils/web3Cache')
@ -13,9 +13,19 @@ function countInteractions(requests) {
stats[msg.sender] = {} stats[msg.sender] = {}
} }
if (!stats[msg.sender][msg.executor]) { if (!stats[msg.sender][msg.executor]) {
stats[msg.sender][msg.executor] = 0 stats[msg.sender][msg.executor] = {
success: 0,
failed: 0,
pending: 0
}
}
if (msg.status === true) {
stats[msg.sender][msg.executor].success += 1
} else if (msg.status === false) {
stats[msg.sender][msg.executor].failed += 1
} else {
stats[msg.sender][msg.executor].pending += 1
} }
stats[msg.sender][msg.executor] += 1
}) })
return stats return stats
} }
@ -80,14 +90,8 @@ async function main(mode) {
homeToForeignConfirmations, homeToForeignConfirmations,
foreignToHomeConfirmations foreignToHomeConfirmations
} = await eventsInfo(mode) } = await eventsInfo(mode)
const homeToForeign = homeToForeignRequests const homeToForeign = homeToForeignRequests.map(normalize).map(addExecutionStatus(homeToForeignConfirmations))
.map(normalize) const foreignToHome = foreignToHomeRequests.map(normalize).map(addExecutionStatus(foreignToHomeConfirmations))
.map(addExecutionStatus(homeToForeignConfirmations))
.filter(x => typeof x.status === 'boolean')
const foreignToHome = foreignToHomeRequests
.map(normalize)
.map(addExecutionStatus(foreignToHomeConfirmations))
.filter(x => typeof x.status === 'boolean')
for (const event of homeToForeign) { for (const event of homeToForeign) {
// AMB contract emits a single UserRequestForSignature event for every home->foreign request. // AMB contract emits a single UserRequestForSignature event for every home->foreign request.
@ -98,7 +102,7 @@ async function main(mode) {
// Executor is definitely a contract if a message execution failed, since message calls to EOA always succeed. // Executor is definitely a contract if a message execution failed, since message calls to EOA always succeed.
// Alternatively, the executor is checked to be a contract by looking at its bytecode size. // Alternatively, the executor is checked to be a contract by looking at its bytecode size.
event.isExecutorAContract = !event.status || (await isForeignContract(event.executor)) event.isExecutorAContract = event.status === false || (await isForeignContract(event.executor))
} }
for (const event of foreignToHome) { for (const event of foreignToHome) {
// AMB contract emits a single UserRequestForAffirmation event for every foreign->home request. // AMB contract emits a single UserRequestForAffirmation event for every foreign->home request.
@ -109,7 +113,7 @@ async function main(mode) {
// Executor is definitely a contract if a message execution failed, since message calls to EOA always succeed. // Executor is definitely a contract if a message execution failed, since message calls to EOA always succeed.
// Alternatively, the executor is checked to be a contract by looking at its bytecode size. // Alternatively, the executor is checked to be a contract by looking at its bytecode size.
event.isExecutorAContract = !event.status || (await isHomeContract(event.executor)) event.isExecutorAContract = event.status === false || (await isHomeContract(event.executor))
} }
const C2C = event => event.isSenderAContract && event.isExecutorAContract const C2C = event => event.isSenderAContract && event.isExecutorAContract
const U2C = event => !event.isSenderAContract && event.isExecutorAContract const U2C = event => !event.isSenderAContract && event.isExecutorAContract

@ -1,5 +1,5 @@
require('dotenv').config() require('dotenv').config()
const logger = require('./logger')('getBalances') const logger = require('./logger')('metrics')
const { readFile } = require('./utils/file') const { readFile } = require('./utils/file')
const { const {

@ -156,6 +156,7 @@ function saveCache() {
if (isDirty) { if (isDirty) {
logger.debug('Saving cache on disk') logger.debug('Saving cache on disk')
writeCacheFile(homeTxSendersCacheFile, cachedHomeTxSenders) writeCacheFile(homeTxSendersCacheFile, cachedHomeTxSenders)
writeCacheFile(foreignTxSendersCacheFile, cachedForeignTxSenders)
writeCacheFile(homeIsContractCacheFile, cachedHomeIsContract) writeCacheFile(homeIsContractCacheFile, cachedHomeIsContract)
writeCacheFile(foreignIsContractCacheFile, cachedForeignIsContract) writeCacheFile(foreignIsContractCacheFile, cachedForeignIsContract)
} }

@ -6,7 +6,10 @@
"scripts": { "scripts": {
"start": "mocha", "start": "mocha",
"lint": "eslint . --ignore-path ../.eslintignore", "lint": "eslint . --ignore-path ../.eslintignore",
"amb": "ULTIMATE=true mocha test/amb.js", "amb": "mocha test/amb.js",
"native-to-erc": "mocha test/nativeToErc.js",
"erc-to-erc": "mocha test/ercToErc.js",
"erc-to-native": "mocha test/ercToNative.js",
"alm": "mocha test/amb.js" "alm": "mocha test/amb.js"
}, },
"author": "", "author": "",

@ -28,30 +28,33 @@ describe('arbitrary message bridging', () => {
let requiredSignatures = 1 let requiredSignatures = 1
before(async () => { before(async () => {
// Only 1 validator is used in ultimate tests // Only 1 validator is used in ultimate tests
if (process.env.ULTIMATE !== 'true') { if (process.env.ULTIMATE === 'true') {
requiredSignatures = 2 return
// Set 2 required signatures for home bridge
await setRequiredSignatures({
bridgeContract: homeBridge,
web3: homeWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
// Set 2 required signatures for foreign bridge
await setRequiredSignatures({
bridgeContract: foreignBridge,
web3: foreignWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
} }
console.log('Calling setRequiredSignatures(2)')
requiredSignatures = 2
// Set 2 required signatures for home bridge
await setRequiredSignatures({
bridgeContract: homeBridge,
web3: homeWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
// Set 2 required signatures for foreign bridge
await setRequiredSignatures({
bridgeContract: foreignBridge,
web3: foreignWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
}) })
describe('Home to Foreign', () => { describe('Home to Foreign', () => {
describe('Subsidized Mode', () => { describe('Subsidized Mode', () => {

@ -25,6 +25,11 @@ const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BR
describe('erc to erc', () => { describe('erc to erc', () => {
before(async () => { before(async () => {
if (process.env.ULTIMATE === 'true') {
return
}
console.log('Calling setRequiredSignatures(2)')
// Set 2 required signatures for home bridge // Set 2 required signatures for home bridge
await setRequiredSignatures({ await setRequiredSignatures({
bridgeContract: homeBridge, bridgeContract: homeBridge,

@ -34,6 +34,11 @@ const homeBridge = new homeWeb3.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME
describe('erc to native', () => { describe('erc to native', () => {
before(async () => { before(async () => {
if (process.env.ULTIMATE === 'true') {
return
}
console.log('Calling setRequiredSignatures(2)')
// Set 2 required signatures for home bridge // Set 2 required signatures for home bridge
await setRequiredSignatures({ await setRequiredSignatures({
bridgeContract: homeBridge, bridgeContract: homeBridge,
@ -144,47 +149,53 @@ describe('erc to native', () => {
} }
}) })
}) })
it('should not process transaction from blocked users', async () => { // allowance/block lists files are not mounted to the host during the ultimate test
const originalBalance1 = await erc20Token.methods.balanceOf(user.address).call() if (process.env.ULTIMATE !== 'true') {
const originalBalance2 = await erc20Token.methods.balanceOf(blockedUser.address).call() it('should not process transaction from blocked users', async () => {
const originalBalance1 = await erc20Token.methods.balanceOf(user.address).call()
const originalBalance2 = await erc20Token.methods.balanceOf(blockedUser.address).call()
// check that account has tokens in home chain // check that account has tokens in home chain
const balance1 = await homeWeb3.eth.getBalance(user.address) const balance1 = await homeWeb3.eth.getBalance(user.address)
const balance2 = await homeWeb3.eth.getBalance(blockedUser.address) const balance2 = await homeWeb3.eth.getBalance(blockedUser.address)
assert(!toBN(balance1).isZero(), 'Account should have tokens') assert(!toBN(balance1).isZero(), 'Account should have tokens')
assert(!toBN(balance2).isZero(), 'Account should have tokens') assert(!toBN(balance2).isZero(), 'Account should have tokens')
// send transaction to home bridge // send transaction to home bridge
await homeWeb3.eth.sendTransaction({ await homeWeb3.eth.sendTransaction({
from: user.address, from: user.address,
to: COMMON_HOME_BRIDGE_ADDRESS, to: COMMON_HOME_BRIDGE_ADDRESS,
gasPrice: '1', gasPrice: '1',
gas: '1000000', gas: '1000000',
value: homeWeb3.utils.toWei('0.01') value: homeWeb3.utils.toWei('0.01')
})
// send transaction to home bridge
await homeWeb3.eth.sendTransaction({
from: blockedUser.address,
to: COMMON_HOME_BRIDGE_ADDRESS,
gasPrice: '1',
gas: '1000000',
value: homeWeb3.utils.toWei('0.01')
})
// check that balance increases
await uniformRetry(async retry => {
const balance = await erc20Token.methods.balanceOf(user.address).call()
if (toBN(balance).lte(toBN(originalBalance1))) {
retry()
}
})
await sleep(3000)
const balance = await erc20Token.methods.balanceOf(blockedUser.address).call()
assert(
toBN(balance).eq(toBN(originalBalance2)),
'Bridge should not process collected signatures from blocked user'
)
}) })
}
// send transaction to home bridge
await homeWeb3.eth.sendTransaction({
from: blockedUser.address,
to: COMMON_HOME_BRIDGE_ADDRESS,
gasPrice: '1',
gas: '1000000',
value: homeWeb3.utils.toWei('0.01')
})
// check that balance increases
await uniformRetry(async retry => {
const balance = await erc20Token.methods.balanceOf(user.address).call()
if (toBN(balance).lte(toBN(originalBalance1))) {
retry()
}
})
await sleep(3000)
const balance = await erc20Token.methods.balanceOf(blockedUser.address).call()
assert(toBN(balance).eq(toBN(originalBalance2)), 'Bridge should not process collected signatures from blocked user')
})
it('should not invest dai when chai token is disabled', async () => { it('should not invest dai when chai token is disabled', async () => {
const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call() const bridgeDaiTokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
@ -273,121 +284,123 @@ describe('erc to native', () => {
}) })
}) })
describe('handling of chai swaps', async () => { if (process.env.ULTIMATE !== 'true') {
before(async () => { describe('handling of chai swaps', async () => {
// Next tests check validator nonces, this will force every validator to submit signature/affirmation before(async () => {
// Set 3 required signatures for home bridge // Next tests check validator nonces, this will force every validator to submit signature/affirmation
await setRequiredSignatures({ // Set 3 required signatures for home bridge
bridgeContract: homeBridge, await setRequiredSignatures({
web3: homeWeb3, bridgeContract: homeBridge,
requiredSignatures: 3, web3: homeWeb3,
options: { requiredSignatures: 3,
from: validator.address, options: {
gas: '4000000' from: validator.address,
} gas: '4000000'
}
})
// Set 3 required signatures for foreign bridge
await setRequiredSignatures({
bridgeContract: foreignBridge,
web3: foreignWeb3,
requiredSignatures: 3,
options: {
from: validator.address,
gas: '4000000'
}
})
}) })
// Set 3 required signatures for foreign bridge it('should not handle transfer event in paying interest', async () => {
await setRequiredSignatures({ await foreignBridge.methods.setInterestReceiver(user.address).send({
bridgeContract: foreignBridge,
web3: foreignWeb3,
requiredSignatures: 3,
options: {
from: validator.address, from: validator.address,
gas: '4000000' gas: '1000000'
} })
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
await foreignBridge.methods.payInterest().send({
from: user.address,
gas: '1000000'
})
await promiseRetry(async (retry, number) => {
if (number < 6) {
retry()
} else {
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
assert(
nonce === initialNonce,
'Validator should not process transfer event originated during converting Chai => Dai'
)
}
})
})
it('should not handle chai withdrawal transfer event in executeSignatures as a regular transfer', async () => {
await foreignBridge.methods.setMinDaiTokenBalance('0').send({
from: validator.address,
gas: '1000000'
}) // set investing limit to 0
await foreignBridge.methods.convertDaiToChai().send({
from: validator.address,
gas: '1000000'
}) // convert all existing dai tokens on bridge account to chai, in order to start from zero balance
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
const originalBalance = await erc20Token.methods.balanceOf(user.address).call()
// send transaction to home bridge
await homeWeb3.eth.sendTransaction({
from: user.address,
to: COMMON_HOME_BRIDGE_ADDRESS,
gasPrice: '1',
gas: '1000000',
value: homeWeb3.utils.toWei('0.01')
})
// check that balance increases
await uniformRetry(async retry => {
const balance = await erc20Token.methods.balanceOf(user.address).call()
if (toBN(balance).lte(toBN(originalBalance))) {
retry()
}
})
await promiseRetry(async (retry, number) => {
if (number < 6) {
retry()
} else {
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
assert(
nonce === initialNonce + 1,
'Validator should not process transfer event originated during converting Chai => Dai'
)
}
})
})
after(async () => {
// Set 2 required signatures for home bridge
await setRequiredSignatures({
bridgeContract: homeBridge,
web3: homeWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
// Set 2 required signatures for foreign bridge
await setRequiredSignatures({
bridgeContract: foreignBridge,
web3: foreignWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
}) })
}) })
}
it('should not handle transfer event in paying interest', async () => {
await foreignBridge.methods.setInterestReceiver(user.address).send({
from: validator.address,
gas: '1000000'
})
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
await foreignBridge.methods.payInterest().send({
from: user.address,
gas: '1000000'
})
await promiseRetry(async (retry, number) => {
if (number < 6) {
retry()
} else {
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
assert(
nonce === initialNonce,
'Validator should not process transfer event originated during converting Chai => Dai'
)
}
})
})
it('should not handle chai withdrawal transfer event in executeSignatures as a regular transfer', async () => {
await foreignBridge.methods.setMinDaiTokenBalance('0').send({
from: validator.address,
gas: '1000000'
}) // set investing limit to 0
await foreignBridge.methods.convertDaiToChai().send({
from: validator.address,
gas: '1000000'
}) // convert all existing dai tokens on bridge account to chai, in order to start from zero balance
const initialNonce = await homeWeb3.eth.getTransactionCount(validator.address)
const originalBalance = await erc20Token.methods.balanceOf(user.address).call()
// send transaction to home bridge
await homeWeb3.eth.sendTransaction({
from: user.address,
to: COMMON_HOME_BRIDGE_ADDRESS,
gasPrice: '1',
gas: '1000000',
value: homeWeb3.utils.toWei('0.01')
})
// check that balance increases
await uniformRetry(async retry => {
const balance = await erc20Token.methods.balanceOf(user.address).call()
if (toBN(balance).lte(toBN(originalBalance))) {
retry()
}
})
await promiseRetry(async (retry, number) => {
if (number < 6) {
retry()
} else {
const nonce = await homeWeb3.eth.getTransactionCount(validator.address)
assert(
nonce === initialNonce + 1,
'Validator should not process transfer event originated during converting Chai => Dai'
)
}
})
})
after(async () => {
// Set 2 required signatures for home bridge
await setRequiredSignatures({
bridgeContract: homeBridge,
web3: homeWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
// Set 2 required signatures for foreign bridge
await setRequiredSignatures({
bridgeContract: foreignBridge,
web3: foreignWeb3,
requiredSignatures: 2,
options: {
from: validator.address,
gas: '4000000'
}
})
})
})
}) })

@ -46,6 +46,11 @@ const foreignBridge = new foreignWeb3.eth.Contract(FOREIGN_NATIVE_TO_ERC_ABI, CO
describe('native to erc', () => { describe('native to erc', () => {
before(async () => { before(async () => {
if (process.env.ULTIMATE === 'true') {
return
}
console.log('Calling setRequiredSignatures(2)')
// Set 2 required signatures for home bridge // Set 2 required signatures for home bridge
await setRequiredSignatures({ await setRequiredSignatures({
bridgeContract: homeBridge, bridgeContract: homeBridge,

@ -15,6 +15,7 @@ COMMON_HOME_GAS_PRICE_FALLBACK=1000000000
ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL=600000 ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL=600000
COMMON_HOME_GAS_PRICE_FACTOR=1 COMMON_HOME_GAS_PRICE_FACTOR=1
ORACLE_HOME_TX_RESEND_INTERVAL=300000 ORACLE_HOME_TX_RESEND_INTERVAL=300000
ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT=
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
@ -22,6 +23,7 @@ COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000
ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000 ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1 COMMON_FOREIGN_GAS_PRICE_FACTOR=1
ORACLE_FOREIGN_TX_RESEND_INTERVAL=1200000 ORACLE_FOREIGN_TX_RESEND_INTERVAL=1200000
ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT=4000
ORACLE_QUEUE_URL=amqp://rabbit ORACLE_QUEUE_URL=amqp://rabbit
ORACLE_REDIS_URL=redis://redis ORACLE_REDIS_URL=redis://redis

@ -150,10 +150,6 @@ In case you need to reset your bridge or setup a new one (with different configu
- `yarn sender:home` - `yarn sender:home`
- `yarn sender:foreign` - `yarn sender:foreign`
### Bridge UI
See the [UI instructions](../ui/README.md) to configure and use the optional Bridge UI.
### Build the image without running the oracle ### Build the image without running the oracle
To build the image change the directory: To build the image change the directory:

@ -73,6 +73,8 @@ const bridgeConfig = {
shutdownKey: 'oracle-shutdown' shutdownKey: 'oracle-shutdown'
} }
const toBNOrNull = x => (x ? toBN(x) : null)
const homeConfig = { const homeConfig = {
chain: 'home', chain: 'home',
eventContractAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS, eventContractAddress: process.env.COMMON_HOME_BRIDGE_ADDRESS,
@ -81,6 +83,7 @@ const homeConfig = {
bridgeAbi: homeAbi, bridgeAbi: homeAbi,
pollingInterval: process.env.ORACLE_HOME_RPC_POLLING_INTERVAL, pollingInterval: process.env.ORACLE_HOME_RPC_POLLING_INTERVAL,
startBlock: toBN(process.env.ORACLE_HOME_START_BLOCK || 0), startBlock: toBN(process.env.ORACLE_HOME_START_BLOCK || 0),
blockPollingLimit: toBNOrNull(process.env.ORACLE_HOME_RPC_BLOCK_POLLING_LIMIT),
web3: web3Home web3: web3Home
} }
@ -92,6 +95,7 @@ const foreignConfig = {
bridgeAbi: foreignAbi, bridgeAbi: foreignAbi,
pollingInterval: process.env.ORACLE_FOREIGN_RPC_POLLING_INTERVAL, pollingInterval: process.env.ORACLE_FOREIGN_RPC_POLLING_INTERVAL,
startBlock: toBN(process.env.ORACLE_FOREIGN_START_BLOCK || 0), startBlock: toBN(process.env.ORACLE_FOREIGN_START_BLOCK || 0),
blockPollingLimit: toBNOrNull(process.env.ORACLE_FOREIGN_RPC_BLOCK_POLLING_LIMIT),
web3: web3Foreign web3: web3Foreign
} }

@ -13,6 +13,7 @@ const {
privateKeyToAddress, privateKeyToAddress,
syncForEach, syncForEach,
waitForFunds, waitForFunds,
waitForUnsuspend,
watchdog, watchdog,
nonceError nonceError
} = require('./utils/utils') } = require('./utils/utils')
@ -45,30 +46,40 @@ async function initialize() {
GasPrice.start(config.id) GasPrice.start(config.id)
chainId = await getChainId(web3Instance) chainId = await getChainId(web3Instance)
connectSenderToQueue({
queueName: config.queue,
oldQueueName: config.oldQueue,
resendInterval: config.resendInterval,
cb: options => {
if (config.maxProcessingTime) {
return watchdog(() => main(options), config.maxProcessingTime, () => {
logger.fatal('Max processing time reached')
process.exit(EXIT_CODES.MAX_TIME_REACHED)
})
}
return main(options) connectQueue()
}
})
} catch (e) { } catch (e) {
logger.error(e.message) logger.error(e.message)
process.exit(EXIT_CODES.GENERAL_ERROR) process.exit(EXIT_CODES.GENERAL_ERROR)
} }
} }
function connectQueue() {
connectSenderToQueue({
queueName: config.queue,
oldQueueName: config.oldQueue,
resendInterval: config.resendInterval,
cb: options => {
if (config.maxProcessingTime) {
return watchdog(() => main(options), config.maxProcessingTime, () => {
logger.fatal('Max processing time reached')
process.exit(EXIT_CODES.MAX_TIME_REACHED)
})
}
return main(options)
}
})
}
function resume(newBalance) { function resume(newBalance) {
logger.info(`Validator balance changed. New balance is ${newBalance}. Resume messages processing.`) logger.info(`Validator balance changed. New balance is ${newBalance}. Resume messages processing.`)
initialize() connectQueue()
}
function unsuspend() {
logger.info(`Oracle sender was unsuspended.`)
connectQueue()
} }
async function readNonce(forceUpdate) { async function readNonce(forceUpdate) {
@ -103,11 +114,10 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
return return
} }
const wasShutdown = await getShutdownFlag(logger, config.shutdownKey, false)
if (await getShutdownFlag(logger, config.shutdownKey, true)) { if (await getShutdownFlag(logger, config.shutdownKey, true)) {
if (!wasShutdown) { logger.info('Oracle sender was suspended via the remote shutdown process')
logger.info('Oracle sender was suspended via the remote shutdown process') channel.close()
} waitForUnsuspend(() => getShutdownFlag(logger, config.shutdownKey, true), unsuspend)
return return
} }
@ -185,7 +195,11 @@ async function main({ msg, ackMsg, nackMsg, channel, scheduleForRetry, scheduleT
) )
const message = e.message.toLowerCase() const message = e.message.toLowerCase()
if (isResend || message.includes('transaction with the same hash was already imported')) { if (message.includes('replacement transaction underpriced')) {
logger.info('Replacement transaction underpriced, forcing gas price update')
GasPrice.start(config.id)
failedTx.push(job)
} else if (isResend || message.includes('transaction with the same hash was already imported')) {
resendJobs.push(job) resendJobs.push(job)
} else { } else {
// if initial transaction sending has failed not due to the same hash error // if initial transaction sending has failed not due to the same hash error

@ -73,17 +73,20 @@ async function start(chainId, fetchOnce) {
throw new Error(`Unrecognized chainId '${chainId}'`) throw new Error(`Unrecognized chainId '${chainId}'`)
} }
const fetchFn = gasPriceSupplierUrl === 'gas-price-oracle' ? null : () => fetch(gasPriceSupplierUrl) let fetchFn = null
if (fetchOnce) { if (gasPriceSupplierUrl !== 'gas-price-oracle') {
await fetchGasPrice(speedType, factor, bridgeContract, fetchFn) fetchFn = () => fetch(gasPriceSupplierUrl, { timeout: 2000 })
return getPrice()
} }
fetchGasPriceInterval = setIntervalAndRun( if (fetchOnce) {
() => fetchGasPrice(speedType, factor, bridgeContract, fetchFn), await fetchGasPrice(speedType, factor, bridgeContract, fetchFn)
updateInterval } else {
) fetchGasPriceInterval = await setIntervalAndRun(
return null () => fetchGasPrice(speedType, factor, bridgeContract, fetchFn),
updateInterval
)
}
return getPrice()
} }
function getPrice() { function getPrice() {

@ -43,7 +43,7 @@ async function fetchShutdownFlag() {
} }
if (config.shutdownContractAddress) { if (config.shutdownContractAddress) {
const shutdownSelector = web3Side.eth.abi.encodeEventSignature(config.shutdownMethod) const shutdownSelector = web3Side.eth.abi.encodeFunctionSignature(config.shutdownMethod)
logger.debug( logger.debug(
{ contract: config.shutdownContractAddress, method: config.shutdownMethod, data: shutdownSelector }, { contract: config.shutdownContractAddress, method: config.shutdownMethod, data: shutdownSelector },
'Fetching shutdown status from contract' 'Fetching shutdown status from contract'

@ -29,24 +29,30 @@ function checkHTTPS(ORACLE_ALLOW_HTTP_FOR_RPC, logger) {
} }
} }
const promiseRetryForever = f => promiseRetry(f, { forever: true, factor: 1 })
async function waitForFunds(web3, address, minimumBalance, cb, logger) { async function waitForFunds(web3, address, minimumBalance, cb, logger) {
promiseRetry( promiseRetryForever(async retry => {
async retry => { logger.debug('Getting balance of validator account')
logger.debug('Getting balance of validator account') const newBalance = web3.utils.toBN(await web3.eth.getBalance(address))
const newBalance = web3.utils.toBN(await web3.eth.getBalance(address)) if (newBalance.gte(web3.utils.toBN(minimumBalance.toString(10)))) {
if (newBalance.gte(web3.utils.toBN(minimumBalance.toString(10)))) { logger.debug({ balance: newBalance, minimumBalance }, 'Validator has minimum necessary balance')
logger.debug({ balance: newBalance, minimumBalance }, 'Validator has minimum necessary balance') cb(newBalance)
cb(newBalance) } else {
} else { logger.debug({ balance: newBalance, minimumBalance }, 'Balance of validator is still less than the minimum')
logger.debug({ balance: newBalance, minimumBalance }, 'Balance of validator is still less than the minimum') retry()
retry()
}
},
{
forever: true,
factor: 1
} }
) })
}
async function waitForUnsuspend(getSuspendFlag, cb) {
promiseRetryForever(async retry => {
if (await getSuspendFlag()) {
retry()
} else {
cb()
}
})
} }
function addExtraGas(gas, extraPercentage, maxGasLimit = Infinity) { function addExtraGas(gas, extraPercentage, maxGasLimit = Infinity) {
@ -58,9 +64,9 @@ function addExtraGas(gas, extraPercentage, maxGasLimit = Infinity) {
return BigNumber.min(maxGasLimit, gasWithExtra) return BigNumber.min(maxGasLimit, gasWithExtra)
} }
function setIntervalAndRun(f, interval) { async function setIntervalAndRun(f, interval) {
const handler = setInterval(f, interval) const handler = setInterval(f, interval)
f() await f()
return handler return handler
} }
@ -135,6 +141,7 @@ module.exports = {
syncForEach, syncForEach,
checkHTTPS, checkHTTPS,
waitForFunds, waitForFunds,
waitForUnsuspend,
addExtraGas, addExtraGas,
setIntervalAndRun, setIntervalAndRun,
watchdog, watchdog,

@ -164,6 +164,8 @@ async function main({ sendToQueue, sendToWorker }) {
logger.info('Oracle watcher was suspended via the remote shutdown process') logger.info('Oracle watcher was suspended via the remote shutdown process')
} }
return return
} else if (wasShutdown) {
logger.info(`Oracle watcher was unsuspended.`)
} }
await checkConditions() await checkConditions()
@ -176,7 +178,8 @@ async function main({ sendToQueue, sendToWorker }) {
} }
const fromBlock = lastProcessedBlock.add(ONE) const fromBlock = lastProcessedBlock.add(ONE)
const toBlock = lastBlockToProcess const rangeEndBlock = config.blockPollingLimit ? fromBlock.add(config.blockPollingLimit) : lastBlockToProcess
const toBlock = BN.min(lastBlockToProcess, rangeEndBlock)
const events = await getEvents({ const events = await getEvents({
contract: eventContract, contract: eventContract,
@ -200,8 +203,8 @@ async function main({ sendToQueue, sendToWorker }) {
} }
} }
logger.debug({ lastProcessedBlock: lastBlockToProcess.toString() }, 'Updating last processed block') logger.debug({ lastProcessedBlock: toBlock.toString() }, 'Updating last processed block')
await updateLastProcessedBlock(lastBlockToProcess) await updateLastProcessedBlock(toBlock)
} catch (e) { } catch (e) {
logger.error(e) logger.error(e)
} }

@ -24,8 +24,6 @@
"commons", "commons",
"oracle", "oracle",
"oracle-e2e", "oracle-e2e",
"ui",
"ui-e2e",
"monitor", "monitor",
"monitor-e2e", "monitor-e2e",
"contracts", "contracts",
@ -35,15 +33,12 @@
], ],
"scripts": { "scripts": {
"initialize": "yarn clean && git submodule update --init && yarn install --unsafe-perm --frozen-lockfile && yarn install:deploy && yarn compile:contracts", "initialize": "yarn clean && git submodule update --init && yarn install --unsafe-perm --frozen-lockfile && yarn install:deploy && yarn compile:contracts",
"build": "yarn build:ui && yarn build:alm && yarn build:plugin", "build": "yarn build:alm && yarn build:plugin",
"build:ui": "yarn workspace ui run build",
"build:alm": "yarn workspace alm run build", "build:alm": "yarn workspace alm run build",
"build:plugin": "yarn workspace burner-wallet-plugin run build", "build:plugin": "yarn workspace burner-wallet-plugin run build",
"lint": "yarn wsrun --exclude tokenbridge-contracts lint", "lint": "yarn wsrun --exclude tokenbridge-contracts lint",
"test": "yarn wsrun --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e --exclude alm-e2e test", "test": "yarn wsrun --exclude oracle-e2e --exclude monitor-e2e --exclude alm-e2e test",
"oracle-e2e": "./oracle-e2e/run-tests.sh", "oracle-e2e": "./oracle-e2e/run-tests.sh",
"ui-e2e": "./ui-e2e/run-tests.sh",
"ui-e2e:ci": "xvfb-run yarn ui-e2e",
"monitor-e2e": "./monitor-e2e/run-tests.sh", "monitor-e2e": "./monitor-e2e/run-tests.sh",
"alm-e2e": "./alm-e2e/run-tests.sh", "alm-e2e": "./alm-e2e/run-tests.sh",
"clean": "rm -rf ./node_modules ./**/node_modules ./**/**/node_modules ./**/build ./**/**/dist", "clean": "rm -rf ./node_modules ./**/node_modules ./**/**/node_modules ./**/build ./**/**/dist",

@ -1,16 +0,0 @@
{
"extends": [
"plugin:node/recommended",
"airbnb-base",
"../.eslintrc"
],
"plugins": ["node"],
"rules": {
"node/no-unpublished-require": "off",
"import/no-extraneous-dependencies": "off",
"no-return-await": "off"
},
"env": {
"mocha": true
}
}

Binary file not shown.

@ -1,160 +0,0 @@
const { Key } = require('selenium-webdriver')
const { By } = require('selenium-webdriver/lib/by')
const { Page } = require('./Page.js')
const { homeRPC, foreignRPC } = require('../e2e-commons/constants.json')
const IDMetaMask = 'hccmbhdehlhjhkenmcjnbcahkmljpife'
const URL = 'chrome-extension://' + IDMetaMask + '//popup.html'
const buttonSubmit = By.className('confirm btn-green')
const buttonAccept = By.xpath('//*[@id="app-content"]/div/div[4]/div/button')
const agreement = By.xpath('//*[@id="app-content"]/div/div[4]/div/div/div/p[1]/strong')
const fieldNewPass = By.xpath('//*[@id="password-box"]')
const fieldConfirmPass = By.xpath('//*[@id="password-box-confirm"]')
const buttonCreate = By.xpath('//*[@id="app-content"]/div/div[4]/div/button')
const buttonIveCopied = By.xpath('//*[@id="app-content"]/div/div[4]/div/button[1]')
const popupNetwork = By.className('network-name')
const popupAccount = By.xpath('//*[@id="app-content"]/div/div[1]/div/div[2]/span/div')
const fieldPrivateKey = By.xpath('//*[@id="private-key-box"]')
const pass = 'qwerty12345'
const buttonImport = By.xpath('//*[@id="app-content"]/div/div[4]/div/div[3]/button')
const fieldNewRPCURL = By.id('new_rpc')
const buttonSave = By.xpath('//*[@id="app-content"]/div/div[4]/div/div[3]/div/div[2]/button')
const arrowBackRPCURL = By.xpath('//*[@id="app-content"]/div/div[4]/div/div[1]/i')
const iconChangeAccount = By.className('cursor-pointer color-orange accounts-selector')
let accountOrderNumber = 1
const networks = [0, 3, 43, 4, 8545]
class MetaMask extends Page {
constructor(driver) {
super(driver)
this.driver = driver
this.URL = URL
}
async clickButtonSubmitTransaction() {
return this.clickWithWait(buttonSubmit)
}
async activate() {
return (
(await this.switchToNextPage()) &&
(await this.open(this.URL)) === this.URL &&
(await this.clickWithWait(buttonAccept)) &&
(await this.clickWithWait(agreement)) &&
(await this.clickKey(Key.TAB, 15)) &&
(await this.clickWithWait(buttonAccept)) &&
(await this.waitUntilLocated(fieldNewPass)) &&
(await this.clickWithWait(fieldNewPass)) &&
(await this.fillWithWait(fieldNewPass, pass)) &&
(await this.fillWithWait(fieldConfirmPass, pass)) &&
(await this.clickWithWait(buttonCreate)) &&
(await this.waitUntilDisplayed(buttonIveCopied)) &&
(await this.clickWithWait(buttonIveCopied)) &&
(await this.switchToNextPage())
)
}
async importAccount(user) {
user.accountOrderInMetamask = accountOrderNumber - 1
return (
(await this.switchToNextPage()) &&
(await this.setNetwork(user.networkID)) &&
(await this.clickImportAccount()) &&
(await this.fillWithWait(fieldPrivateKey, user.privateKey)) &&
(await this.waitUntilDisplayed(buttonImport)) &&
(await this.clickWithWait(buttonImport)) &&
(await this.switchToNextPage())
)
}
async selectAccount(user) {
try {
await this.switchToNextPage()
await this.setNetwork(user.networkID)
await super.clickWithWait(popupAccount)
await this.driver.executeScript(
"document.getElementsByClassName('dropdown-menu-item')[" + user.accountOrderInMetamask + '].click();'
)
await this.switchToNextPage()
return true
} catch (err) {
return false
}
}
async clickImportAccount() {
try {
await super.clickWithWait(popupAccount)
await this.driver.executeScript(
"document.getElementsByClassName('dropdown-menu-item')[" + (accountOrderNumber + 1) + '].click();'
)
accountOrderNumber++
return true
} catch (err) {
return false
}
}
async signTransaction(refreshCount) {
await this.switchToNextPage()
let counter = 5
if (refreshCount !== undefined) counter = refreshCount
do {
await this.refresh()
await super.waitUntilLocated(iconChangeAccount)
if (await this.isElementDisplayed(buttonSubmit)) {
return (await this.clickButtonSubmitTransaction()) && (await this.switchToNextPage())
}
await this.driver.sleep(3000)
} while (counter-- >= 0)
await this.switchToNextPage()
return false
}
async setNetwork(provider) {
try {
await super.clickWithWait(popupNetwork)
const orderNumber = networks.indexOf(provider)
const script = "document.getElementsByClassName('dropdown-menu-item')[" + orderNumber + '].click();'
if (orderNumber < 0) await this.addNetwork(provider)
else await this.driver.executeScript(script)
return true
} catch (err) {
return false
}
}
async addNetwork(provider) {
let url
switch (provider) {
case homeRPC.ID: {
url = 'http://localhost:8541'
networks.push(177)
break
}
case foreignRPC.ID: {
url = 'http://localhost:8542'
networks.push(142)
break
}
default: {
throw new Error(`Unexcpected provider ${provider}`)
}
}
const index = networks.length > 8 ? 8 : networks.length
await this.driver.executeScript(
"document.getElementsByClassName('dropdown-menu-item')[" + (index - 1) + '].click();'
)
return (
(await super.fillWithWait(fieldNewRPCURL, url)) &&
(await super.clickWithWait(buttonSave)) &&
(await super.clickWithWait(arrowBackRPCURL))
)
}
}
module.exports = {
MetaMask
}

@ -1,149 +0,0 @@
const webdriver = require('selenium-webdriver')
const Twait = 20000
class Page {
constructor(driver) {
this.driver = driver
}
async waitUntilDisplayed(element, Twaiting) {
let counter = Twaiting
if (counter === undefined) counter = 180
try {
do {
await this.driver.sleep(300)
if (await this.isElementDisplayed(element)) return true
} while (counter-- > 0)
return false
} catch (err) {
return false
}
}
async waitUntilDisappear(element, Twaiting) {
let counter = Twaiting
if (counter === undefined) counter = 180
try {
do {
await this.driver.sleep(300)
if (!(await this.isElementDisplayed(element))) return true
} while (counter-- > 0)
return false
} catch (err) {
return false
}
}
async waitUntilLocated(element, Twaiting) {
let counter = Twaiting
if (counter === undefined) counter = 180
try {
do {
await this.driver.sleep(300)
if (await this.isElementLocated(element)) return true
} while (counter-- > 0)
return false
} catch (err) {
return false
}
}
async isElementDisplayed(element) {
try {
return await this.driver.findElement(element).isDisplayed()
} catch (err) {
return false
}
}
async isElementLocated(element) {
return (await this.driver.findElements(element)).length > 0
}
async clickWithWait(element) {
try {
let field
if (element.constructor.name !== 'WebElement') {
field = await this.driver.wait(webdriver.until.elementLocated(element), Twait)
} else field = element
await field.click()
return true
} catch (err) {
return false
}
}
async fillWithWait(element, text) {
try {
let field
if (element.constructor.name !== 'WebElement') {
field = await this.driver.wait(webdriver.until.elementLocated(element), Twait)
} else field = element
await field.sendKeys(text)
return true
} catch (err) {
return false
}
}
async findWithWait(element) {
try {
await this.driver.wait(webdriver.until.elementLocated(element), Twait)
return await this.driver.findElements(element)
} catch (err) {
return null
}
}
async switchToNextPage() {
let allHandles = []
let curHandle
try {
allHandles = await this.driver.getAllWindowHandles()
curHandle = await this.driver.getWindowHandle()
if (allHandles.length > 2) {
allHandles = [allHandles[0], allHandles[1]]
}
let handle
for (let i = 0; i < allHandles.length; i++) {
if (curHandle !== allHandles[i]) {
handle = allHandles[i]
break
}
}
await this.driver.switchTo().window(handle)
await this.driver.sleep(500)
return true
} catch (err) {
return false
}
}
async refresh() {
await this.driver.navigate().refresh()
}
async getUrl() {
return await this.driver.getCurrentUrl()
}
async open(url) {
await this.driver.get(url)
return this.getUrl()
}
async clickKey(key, times) {
try {
const action = this.driver.actions()
for (let i = 0; i < times; i++) await action.sendKeys(key).perform()
return true
} catch (err) {
return false
}
}
}
module.exports = {
Page
}

@ -1,29 +0,0 @@
# POA TokenBridge / UI-E2E
End to end tests for the POA TokenBridge [UI](../UI/README.md).
- Configure startURL, homeAccount, foreignAccount in ```config.json```
## Running
To run the bridge end-to-end tests, you just have to run:
```
./run-tests.sh
```
#### Tests
```
1. User is able to open main page of bridge-ui
2. Main page: foreign POA balance is displayed
3. Main page: home POA balance is displayed
4. User is able to send tokens from Home account to Foreign account
5. Home POA balance has correctly changed after transaction
6. Foreign account has received correct amount of tokens after transaction
7. User is able to send tokens from Foreign account to Home account
8. Foreign POA balance has correctly changed after transaction
9. Home account has received correct amount of tokens after transaction
```

@ -1,52 +0,0 @@
const { MetaMask } = require('./MetaMask.js')
const { MainPage } = require('./mainPage.js')
class User {
constructor(driver, obj) {
try {
this.driver = driver
this.account = obj.account
this.privateKey = obj.privateKey
this.networkID = obj.networkID
this.accountOrderInMetamask = 'undefined' // for MetaMaskPage usage only
} catch (err) {
console.log('instance User was not created')
console.log(err)
}
}
async transferTokens(amount) {
const mainPage = new MainPage(this.driver)
const metaMask = new MetaMask(this.driver)
return (
(await mainPage.fillFieldAmount(amount)) &&
(await mainPage.clickButtonTransfer()) &&
(await mainPage.waitUntilShowUpButtonTransferConfirm()) &&
(await mainPage.clickButtonTransferConfirm()) &&
(await metaMask.signTransaction()) &&
(await mainPage.waitUntilTransactionDone()) &&
(await mainPage.waitUntilShowUpButtonOk()) &&
(await mainPage.clickButtonOk())
)
}
async setMetaMaskNetwork() {
const metaMask = new MetaMask(this.driver)
return (
(await metaMask.switchToNextPage()) &&
(await metaMask.setNetwork(this.networkID)) &&
(await metaMask.switchToNextPage())
)
}
async setMetaMaskAccount() {
const metaMask = new MetaMask(this.driver)
if (this.accountOrderInMetamask === 'undefined') {
return await metaMask.importAccount(this)
} else return await metaMask.selectAccount(this)
}
}
module.exports = {
User
}

@ -1,62 +0,0 @@
const webdriver = require('selenium-webdriver')
const chrome = require('selenium-webdriver/chrome')
const {
user,
nativeToErcBridge,
ercToErcBridge,
ercToNativeBridge,
ambStakeErcToErc,
homeRPC,
foreignRPC
} = require('../e2e-commons/constants.json')
class Utils {
static async getHomeAccount() {
return {
account: user.address,
privateKey: user.privateKey,
networkID: homeRPC.ID
}
}
static async getForeignAccount() {
return {
account: user.address,
privateKey: user.privateKey,
networkID: foreignRPC.ID
}
}
static async getStartURL() {
return nativeToErcBridge.ui
}
static async getErc20StartURL() {
return ercToErcBridge.ui
}
static async getErc20NativeStartURL() {
return ercToNativeBridge.ui
}
static async getAMBStakeStartURL() {
return ambStakeErcToErc.ui
}
static async startBrowserWithMetamask() {
const source = './MetaMask.crx'
const options = new chrome.Options()
await options.addArguments('--no-sandbox')
await options.addArguments('--disable-gpu')
await options.addArguments('--disable-dev-shm-usage')
await options.addArguments('disable-popup-blocking')
await options.addExtensions(source)
const driver = await new webdriver.Builder().withCapabilities(options.toCapabilities()).build()
await driver.sleep(5000)
return driver
}
}
module.exports = {
Utils
}

@ -1,123 +0,0 @@
const { By } = require('selenium-webdriver/lib/by')
const { Page } = require('./Page.js')
const fieldAmount = By.id('amount')
const buttonTransfer = By.className('bridge-form-button')
const buttonOk = By.className('swal-button swal-button--confirm')
const fieldsBalance = By.className('network-balance')
const classWeb3Loaded = By.className('web3-loaded')
const classPendingTransaction = By.className('pending-transaction')
const loadingContainer = By.className('loading-container')
const buttonTransferConfirm = By.className('transfer-confirm')
const buttonDisclaimerConfirm = By.className('disclaimer-confirm')
const checkboxDisclaimer = By.className('disclaimer-checkbox')
const disclaimer = By.className('disclaimer-title')
class MainPage extends Page {
async initFieldsBalance() {
if (!(await this.waitUntilWeb3Loaded())) return null
try {
const array = await super.findWithWait(fieldsBalance)
/* eslint-disable prefer-destructuring */
this.fieldHomePOABalance = array[0]
this.fieldForeignPOABalance = array[1]
/* eslint-enable prefer-destructuring */
return array
} catch (err) {
return null
}
}
async getHomePOABalance() {
await this.initFieldsBalance()
return parseFloat(await this.fieldHomePOABalance.getText())
}
async getForeignPOABalance() {
await this.initFieldsBalance()
return parseFloat(await this.fieldForeignPOABalance.getText())
}
async fillFieldAmount(amount) {
try {
await this.clickWithWait(fieldAmount)
await this.fillWithWait(fieldAmount, amount)
return true
} catch (err) {
return false
}
}
async clickButtonTransfer() {
return this.clickWithWait(buttonTransfer)
}
async clickButtonOk() {
return super.clickWithWait(buttonOk)
}
async clickButtonTransferConfirm() {
return super.clickWithWait(buttonTransferConfirm)
}
async isPresentButtonOk() {
return super.isElementDisplayed(buttonOk, 180)
}
async waitUntilWeb3Loaded() {
return this.waitUntilLocated(classWeb3Loaded, 180)
}
async isPendingTransaction() {
return super.isElementLocated(classPendingTransaction)
}
async waitUntilTransactionDone() {
return this.waitUntilDisappear(classPendingTransaction, 360)
}
async waitUntilShowUpButtonOk() {
return super.waitUntilDisplayed(buttonOk, 360)
}
async waitUntilShowUpButtonTransferConfirm() {
return super.waitUntilDisplayed(buttonTransferConfirm, 360)
}
async waitUntilShowUpLoadingContainer() {
return super.waitUntilDisplayed(loadingContainer, 180)
}
async isDisplayedLoadingContainer() {
return super.isElementDisplayed(loadingContainer)
}
async confirmDisclaimer() {
return (
(await super.waitUntilDisplayed(disclaimer, 180)) &&
// await this.clickCheckboxDisclaimer() &&
(await this.clickButtonDisclaimerConfirm())
)
}
async clickButtonDisclaimerConfirm() {
return super.clickWithWait(buttonDisclaimerConfirm)
}
async clickCheckboxDisclaimer() {
return super.clickWithWait(checkboxDisclaimer)
}
async open(url) {
let counter = 60
do {
await this.driver.sleep(1000)
await super.open(url)
} while (counter-- >= 0 && !(await this.isElementDisplayed(disclaimer)))
return counter >= 0
}
}
module.exports = {
MainPage
}

@ -1,16 +0,0 @@
{
"name": "ui-e2e",
"version": "0.0.1",
"license": "MIT",
"private": true,
"devDependencies": {
"mocha": "^5.2.0",
"selenium-webdriver": "3.6.0"
},
"scripts": {
"lint": "eslint . --ignore-path ../.eslintignore"
},
"engines": {
"node": ">= 10.18"
}
}

@ -1,11 +0,0 @@
#!/usr/bin/env bash
cd $(dirname $0)
../e2e-commons/up.sh deploy oracle ui blocks
yarn mocha -b ./test.js
rc=$?
../e2e-commons/down.sh
exit $rc

@ -1,401 +0,0 @@
const test = require('selenium-webdriver/testing')
const assert = require('assert')
const { Utils } = require('./Utils.js')
const { MetaMask } = require('./MetaMask.js')
const { MainPage } = require('./mainPage.js')
const { User } = require('./User.js')
test.describe('e2e-test for bridge.poa, version 1.5.0', async function() {
this.timeout(5 * 60000)
this.slow(1 * 60000)
this.retries(2)
const maxAmountPerTransactionLimit = 1
let startURL
let driver
let mainPage
let homeAccount
let foreignAccount
let metaMask
let foreignBalanceBefore
let homeBalanceBefore
test.before(async () => {
try {
driver = await Utils.startBrowserWithMetamask()
mainPage = new MainPage(driver)
homeAccount = new User(driver, await Utils.getHomeAccount())
foreignAccount = new User(driver, await Utils.getForeignAccount())
metaMask = new MetaMask(driver)
await metaMask.activate()
await homeAccount.setMetaMaskAccount()
} catch (e) {
console.log(e)
}
})
test.after(async () => {
try {
await driver.quit()
} catch (e) {
console.log(e)
}
})
test.describe('NATIVE TO ERC', async () => {
test.it('User is able to open main page of bridge-ui ', async () => {
startURL = await Utils.getStartURL()
const result = await mainPage.open(startURL)
console.log('Test URL: ' + startURL)
return await assert.strictEqual(result, true, 'Test FAILED. Build failed.')
})
test.it('Home page: disclaimer is displayed ', async () => {
const result = await mainPage.confirmDisclaimer()
return await assert.strictEqual(result, true, 'Test FAILED. Disclaimer is not displayed')
})
test.it('Main page: foreign POA balance is displayed', async () => {
foreignBalanceBefore = await mainPage.getForeignPOABalance()
console.log('foreignBalanceBefore = ' + foreignBalanceBefore)
const result = foreignBalanceBefore === 0
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is zero or not displayed ')
})
test.it('Main page: home POA balance is displayed', async () => {
homeBalanceBefore = await mainPage.getHomePOABalance()
console.log('homeBalanceBefore = ' + homeBalanceBefore)
const result = homeBalanceBefore !== 0
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is zero or not displayed ')
})
test.it('User is able to send tokens from Home account to Foreign account', async () => {
const result = await homeAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Home account to Foreign account'
)
})
test.it('Home POA balance has correctly changed after transaction', async () => {
const newHomeBalance = await mainPage.getHomePOABalance()
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
homeBalanceBefore = newHomeBalance
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
test.it('Foreign account has received correct amount of tokens after transaction ', async () => {
const newForeignBalance = await mainPage.getForeignPOABalance()
const shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
})
test.it('User is able to send tokens from Foreign account to Home account', async () => {
await foreignAccount.setMetaMaskNetwork()
foreignBalanceBefore = await mainPage.getHomePOABalance()
const result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Home account to Foreign account'
)
})
test.it('Foreign POA balance has correctly changed after transaction', async () => {
const newForeignBalance = await mainPage.getHomePOABalance()
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
})
test.it('Home account has received correct amount of tokens after transaction', async () => {
const newHomeBalance = await mainPage.getForeignPOABalance()
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
})
test.describe('ERC TO ERC', async () => {
test.it('User is able to open main page of bridge-ui ', async () => {
await foreignAccount.setMetaMaskNetwork()
startURL = await Utils.getErc20StartURL()
const result = await mainPage.open(startURL)
console.log('Test URL: ' + startURL)
return await assert.strictEqual(result, true, 'Test FAILED. Build failed.')
})
test.it('Home page: disclaimer is displayed ', async () => {
const result = await mainPage.confirmDisclaimer()
return await assert.strictEqual(result, true, 'Test FAILED. Disclaimer is not displayed')
})
test.it('Main page: foreign erc20 balance is displayed', async () => {
foreignBalanceBefore = await mainPage.getForeignPOABalance()
console.log('foreignBalanceBefore = ' + foreignBalanceBefore)
const result = foreignBalanceBefore === 0
return await assert.strictEqual(result, true, 'Test FAILED. Foreign erc20 balance is not zero')
})
test.it('Main page: home erc20 balance is displayed', async () => {
homeBalanceBefore = await mainPage.getHomePOABalance()
console.log('homeBalanceBefore = ' + homeBalanceBefore)
const result = homeBalanceBefore !== 0
return await assert.strictEqual(result, true, 'Test FAILED. Home erc20 balance is zero or not displayed ')
})
test.it('User is able to send tokens from Foreign account to Home account ', async () => {
homeBalanceBefore = await mainPage.getForeignPOABalance()
foreignBalanceBefore = await mainPage.getHomePOABalance()
const result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Foreign account to Home account'
)
})
test.it('Foreign POA balance has correctly changed after transaction', async () => {
const newForeignBalance = await mainPage.getHomePOABalance()
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
})
test.it('Home account has received correct amount of tokens after transaction ', async () => {
const newHomeBalance = await mainPage.getForeignPOABalance()
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
test.it('User is able to send tokens from Home account to Foreign account ', async () => {
await homeAccount.setMetaMaskNetwork()
homeBalanceBefore = await mainPage.getHomePOABalance()
foreignBalanceBefore = await mainPage.getForeignPOABalance()
const result = await homeAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Home account to Foreign account'
)
})
test.it('Home POA balance has correctly changed after transaction', async () => {
const newHomeBalance = await mainPage.getHomePOABalance()
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
homeBalanceBefore = newHomeBalance
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
test.it('Foreign account has received correct amount of tokens after transaction ', async () => {
const newForeignBalance = await mainPage.getForeignPOABalance()
const shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
})
})
test.describe('ERC TO NATIVE', async () => {
test.it('User is able to open main page of bridge-ui ', async () => {
startURL = await Utils.getErc20NativeStartURL()
const result = await mainPage.open(startURL)
console.log('Test URL: ' + startURL)
return await assert.strictEqual(result, true, 'Test FAILED. Build failed.')
})
test.it('Home page: disclaimer is displayed ', async () => {
const result = await mainPage.confirmDisclaimer()
return await assert.strictEqual(result, true, 'Test FAILED. Disclaimer is not displayed')
})
test.it('Main page: foreign erc20 balance is displayed', async () => {
await foreignAccount.setMetaMaskNetwork()
foreignBalanceBefore = await mainPage.getForeignPOABalance()
console.log('foreignBalanceBefore = ' + foreignBalanceBefore)
const result = foreignBalanceBefore !== 0
return await assert.strictEqual(result, true, 'Test FAILED. Foreign erc20 balance is zero')
})
test.it('Main page: home erc20 balance is displayed', async () => {
homeBalanceBefore = await mainPage.getHomePOABalance()
console.log('homeBalanceBefore = ' + homeBalanceBefore)
const result = homeBalanceBefore !== 0
return await assert.strictEqual(result, true, 'Test FAILED. Home erc20 balance is zero or not displayed ')
})
test.it('User is able to send tokens from Foreign account to Home account', async () => {
homeBalanceBefore = await mainPage.getForeignPOABalance()
foreignBalanceBefore = await mainPage.getHomePOABalance()
const result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Foreign account to Home account'
)
})
test.it('Foreign POA balance has correctly changed after transaction', async () => {
const newForeignBalance = await mainPage.getHomePOABalance()
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
})
test.it('Home account has received correct amount of tokens after transaction', async () => {
const newHomeBalance = await mainPage.getForeignPOABalance()
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
test.it('User is able to send tokens from Home account to Foreign account', async () => {
await homeAccount.setMetaMaskNetwork()
homeBalanceBefore = await mainPage.getHomePOABalance()
foreignBalanceBefore = await mainPage.getForeignPOABalance()
const result = await homeAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Home account to Foreign account'
)
})
test.it('Home POA balance has correctly changed after transaction', async () => {
const newHomeBalance = await mainPage.getHomePOABalance()
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
homeBalanceBefore = newHomeBalance
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
test.it('Foreign account has received correct amount of tokens after transaction', async () => {
const newForeignBalance = await mainPage.getForeignPOABalance()
const shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
})
})
test.describe('AMB-STAKE-ERC-TO-ERC', async () => {
test.it('User is able to open main page of bridge-ui ', async () => {
startURL = await Utils.getAMBStakeStartURL()
const result = await mainPage.open(startURL)
console.log('Test URL: ' + startURL)
return await assert.strictEqual(result, true, 'Test FAILED. Build failed.')
})
test.it('Home page: disclaimer is displayed ', async () => {
const result = await mainPage.confirmDisclaimer()
return await assert.strictEqual(result, true, 'Test FAILED. Disclaimer is not displayed')
})
test.it('Main page: foreign erc20 balance is displayed', async () => {
await foreignAccount.setMetaMaskNetwork()
foreignBalanceBefore = await mainPage.getForeignPOABalance()
console.log('foreignBalanceBefore = ' + foreignBalanceBefore)
const result = foreignBalanceBefore !== 0
return await assert.strictEqual(result, true, 'Test FAILED. Foreign erc20 balance is zero')
})
test.it('Main page: home erc20 balance is displayed', async () => {
homeBalanceBefore = await mainPage.getHomePOABalance()
console.log('homeBalanceBefore = ' + homeBalanceBefore)
const result = homeBalanceBefore !== 0
return await assert.strictEqual(result, true, 'Test FAILED. Home erc20 balance is zero or not displayed ')
})
test.it('User is able to send tokens from Foreign account to Home account', async () => {
homeBalanceBefore = await mainPage.getForeignPOABalance()
foreignBalanceBefore = await mainPage.getHomePOABalance()
const result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Foreign account to Home account'
)
})
test.it('Foreign POA balance has correctly changed after transaction', async () => {
const newForeignBalance = await mainPage.getHomePOABalance()
const shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Foreign POA balance is not correct after transaction')
})
test.it('Home account has received correct amount of tokens after transaction', async () => {
const newHomeBalance = await mainPage.getForeignPOABalance()
const shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
test.it('User is able to send tokens from Home account to Foreign account', async () => {
await homeAccount.setMetaMaskNetwork()
homeBalanceBefore = await mainPage.getHomePOABalance()
foreignBalanceBefore = await mainPage.getForeignPOABalance()
const result = await homeAccount.transferTokens(maxAmountPerTransactionLimit)
return await assert.strictEqual(
result,
true,
'Test FAILED. User is able send tokens from Home account to Foreign account'
)
})
test.it('Home POA balance has correctly changed after transaction', async () => {
const newHomeBalance = await mainPage.getHomePOABalance()
const shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit
console.log('newHomeBalance = ' + newHomeBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newHomeBalance) < maxAmountPerTransactionLimit / 10
homeBalanceBefore = newHomeBalance
return await assert.strictEqual(result, true, 'Test FAILED.Home POA balance is not correct after transaction')
})
test.it('Foreign account has received correct amount of tokens after transaction', async () => {
const newForeignBalance = await mainPage.getForeignPOABalance()
const shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit
console.log('newForeignBalance = ' + newForeignBalance)
console.log('shouldBe = ' + shouldBe)
const result = Math.abs(shouldBe - newForeignBalance) < maxAmountPerTransactionLimit / 10
return await assert.strictEqual(result, true, 'Test FAILED. Foreign POA balance is not correct after transaction')
})
})
})

@ -1,14 +0,0 @@
{
"env": {
"test": {
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
}
}
}

@ -1,46 +0,0 @@
COMMON_HOME_BRIDGE_ADDRESS=0xABb4C1399DcC28FBa3Beb76CAE2b50Be3e087353
COMMON_FOREIGN_BRIDGE_ADDRESS=0xE405F6872cE38a7a4Ff63DcF946236D458c2ca3a
COMMON_HOME_RPC_URL=https://sokol.poa.network
COMMON_FOREIGN_RPC_URL=https://kovan.infura.io/mew
UI_NATIVE_TOKEN_DISPLAY_NAME=POA
UI_HOME_NETWORK_DISPLAY_NAME=POA Sokol
UI_FOREIGN_NETWORK_DISPLAY_NAME=Kovan
# Set to true if network doesn't support events
UI_HOME_WITHOUT_EVENTS=false
UI_FOREIGN_WITHOUT_EVENTS=false
UI_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx/%s
UI_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
UI_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard
COMMON_HOME_GAS_PRICE_FALLBACK=5000000000
UI_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_HOME_GAS_PRICE_FACTOR=1
COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/
COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
COMMON_FOREIGN_GAS_PRICE_FALLBACK=5000000000
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
COMMON_FOREIGN_GAS_PRICE_FACTOR=1
# Default
UI_TITLE=TokenBridge UI app - %c
UI_OG_TITLE=POA Bridge UI
UI_DESCRIPTION=The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner.
UI_PORT=3000
UI_PUBLIC_URL=https://bridge.poa.net
# RSK
#UI_DESCRIPTION=The TokenBridge serves as a method of transferring Bancor Network tokens between the Ethereum network and the Rootstock network in a quick and cost-efficient manner.
# To use Ethereum-classic styles: classic
# To use STAKE styles: stake
# To use poa core styles: core
UI_STYLES=core

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

@ -1,15 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "Launch Chrome",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}

@ -1,33 +0,0 @@
FROM node:10 as contracts
WORKDIR /mono
COPY contracts/package.json contracts/package-lock.json ./contracts/
WORKDIR /mono/contracts
RUN npm install --only=prod
COPY ./contracts/truffle-config.js ./
COPY ./contracts/contracts ./contracts
RUN npm run compile
FROM node:10
WORKDIR /mono
COPY package.json .
COPY --from=contracts /mono/contracts/build ./contracts/build
COPY commons/package.json ./commons/
COPY ui/package.json ./ui/
COPY ui/lib/web3-eth/index.js ./ui/lib/web3-eth/index.js
COPY yarn.lock .
RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production
COPY ./commons ./commons
COPY ./ui ./ui
ARG DOT_ENV_PATH=./ui/.env
COPY ${DOT_ENV_PATH} ./ui/.env
WORKDIR /mono/ui
CMD echo "To start a UI application run:" \
"yarn start"

@ -1,197 +0,0 @@
[![Coverage Status](https://coveralls.io/repos/github/poanetwork/tokenbridge/badge.svg?branch=master)](https://coveralls.io/github/poanetwork/tokenbridge?branch=master)
# POA TokenBridge / UI
DApp interface to transfer tokens and coins between chains.
## Overview
Please refer to the [POA TokenBridge](../README.md) overview first of all.
The UI provides an intuitive interface for assets transfer between networks running the Bridge smart contracts. Users can connect to a web3 wallet such as [Nifty Wallet](https://github.com/poanetwork/nifty-wallet) or [MetaMask](https://metamask.io/) and complete the transfer through a web browser.
![Bridge UI](bridge-ui.png)
### UI Features
- Shows daily limits in both networks
- Displays all events in both networks
- Filter events from a specific block number on both sides of the bridge
- Find a corresponding event on different sides of the bridge
- Submit a transaction from Home to Foreign network
- Submit a transaction from Foreign to Home network
### User Transactions
- Connect to the network you want to transfer coins from using a web3 wallet such as Nifty Wallet or MetaMask. This can be the Home or Foreign network.
The wallet must be funded to cover gas costs related to the transfer. With the Native-to-ERC20 bridge, the wallet must contain the amount to transfer, and with the ERC20-to-ERC20 bridge, the wallet must contain tokens linked with the network you are transferring from.
**Process**
- Specify the amount to send.
- Click the `Transfer` button.
- Confirm the transaction via the web3 wallet.
The same address is used to send a coin from the Home network and receive a token on the Foreign Network. In order to send assets in the opposite direction, change the network in the web3 wallet. This changes the bridge interface to show the selected network on the left side of the bridge.
![Web3 Wallet Change Network](web3wallet_network.gif)
### Resources
Some of the following resources are outdated, but provide a general sense of the UI and transactional flow.
- [Deployed URL for POA -> Ethereum Network Bridge](https://bridge.poa.net/)
- [Testnet Bridge URL](https://bridge-testnet.poa.net/)
- [Bridge UI Tutorial Videos](https://www.youtube.com/playlist?list=PLS5SEs8ZftgUqR3hVFiEXQLqE9QI8sIGz)
- [Article on the POA Bridge](https://medium.com/poa-network/cross-chain-bridges-paving-the-way-to-internet-of-blockchains-422ac94bc2e5)
- Wallet Resources
- [MetaMask](https://consensys.zendesk.com/hc/en-us/categories/360001045692-Using-MetaMask)
- [Nifty Wallet](https://poanet.zendesk.com/hc/en-us/articles/360008957634-Nifty-Wallet)
- [AlphaWallet (iOS and Android)](https://alphawallet.com/)
## Getting Started
The following is an example setup using the POA Sokol testnet as the Home network, and the Ethereum Kovan testnet as the Foreign network. The instructions for the Bridge UI are identical for an `ERC20-to-ERC20` configuration, but the smart contract deployment steps will vary.
### Dependencies
- [Smart Contracts](https://github.com/poanetwork/tokenbridge-contracts)
- [Oracle](../oracle/README.md)
- [Node.js](https://nodejs.org/en/download/)
- [AlphaWallet](https://alphawallet.com/) or [Nifty Wallet](https://github.com/poanetwork/nifty-wallet) or [MetaMask](https://metamask.io/)
### Example Setup
1. Create an empty folder for setting up your bridge. In this example we call it `sokol-kovan-bridge`.
`mkdir sokol-kovan-bridge && cd sokol-kovan-bridge`
2. Prepare temporary ETH address(es) for deployment by creating new account(s) in Nifty Wallet or MetaMask. See the [wallet resources](#resources) if you need more information on this step. This account is used:
* for deploying bridge contracts to both networks
* as the bridge contracts management wallet
* as the validator's wallet address(es)
3. Fund the test account(s).
* Fund Home account(s) using the [POA Sokol Faucet](https://faucet.poa.network/)
* Get free Kovan Coins from the [gitter channel](https://gitter.im/kovan-testnet/faucet) or [Iracus faucet](https://github.com/kovan-testnet/faucet) for Foreign account(s). Get 5 Keth to 1 acc, and transfer from there to all other wallets if more than one account is used.
4. Deploy the Sokol <-> Kovan Bridge contracts.
* Go to the the `sokol-kovan-bridge` folder created in step 1 and `git clone https://github.com/poanetwork/tokenbridge-contracts`
* Follow instructions in the [POA Bridge contracts repo](https://github.com/poanetwork/tokenbridge-contracts).
* Set the parameters in the .env file.
* `DEPLOYMENT_ACCOUNT_PRIVATE_KEY`: Export the private key from step 2
* `HOME_RPC_URL`=https://sokol.poa.network
* Wallet address(es) for bridge contracts management. For testing, you can use the same address for all address values in the file. This includes:
* `HOME_OWNER_MULTISIG`
* `HOME_UPGRADEABLE_ADMIN_VALIDATORS`
* `HOME_UPGRADEABLE_ADMIN_BRIDGE`
* `FOREIGN_OWNER_MULTISIG`
* `FOREIGN_UPGRADEABLE_ADMIN_VALIDATORS`
* `FOREIGN_UPGRADEABLE_ADMIN_BRIDGE`
* `VALIDATORS` _Note: Wallet address(es) for validator(s) are separated by a space. For testing, you can use the same address that was used as the bridge contracts management account._
* `FOREIGN_RPC_URL`=https://kovan.infura.io/mew
* When deployment is finished, check that the `bridgeDeploymentResults.json` file exists in the `tokenbridge-contracts/deploy` directory and includes the bridge contract addresses.
5. Install and run the TokenBridge Oracle.
* Go to the `sokol-kovan-bridge` folder
* [Initialize](../README.md#initializing-the-monorepository) the monorepository
* Go to `oracle` sub-repository
* Follow the [Oracle instructions](../oracle/README.md).
If successful, you will see bridge processes run when you issue a command.
For example, run `yarn watcher:signature-request`.
**Example Yarn Output:**
```bash
[1539195000507] INFO (watcher-signature-request): Connected to redis
[1539195000545] INFO (watcher-signature-request): Connected to amqp Broker
[1539195006085] INFO (watcher-signature-request): Found 0 UserRequestForSignature events
[1539195011467] INFO (watcher-signature-request): Found 0 UserRequestForSignature events
```
**Example Docker Output:**
**Note:** The output will depend on your Docker configuration. You may need to access the container logs to view.
```bash
{"level":30,"time":1539366879816,"msg":"Connected to redis","validator":"0x..........","name":"watcher-signature-request","v":1}
{"level":30,"time":1539366879880,"msg":"Connected to amqp Broker","validator":"0x..........","name":"watcher-signature-request","v":1}
{"level":30,"time":1539366885587,"msg":"Found 0 UserRequestForSignature events","validator":"0x..........","name":"watcher-signature-request","v":1}
```
6. Keep the bridge processes running. Open a separate terminal window and go to the `sokol-kovan-bridge` folder to install and run the UI.
* Go to the `sokol-kovan-bridge/tokenbridge` monorepository that was initialized in step **5.**
* Go to `ui` sub-repository
* Create a .env file from the example file [.env.example](.env.example)
```
cp .env.example .env
````
* Insert the addresses from the bridgeDeploymentResults.json file (from step 4) into the .env file. No other changes are needed, see [Env Parameter Details](#env-parameter-details) for information about each parameter.
```
cat ../tokenbridge-contracts/deploy/bridgeDeploymentResults.json
```
```bash
# HomeBridge address in bridgeDeploymentResults.json
COMMON_HOME_BRIDGE_ADDRESS=0x..
# ForeignBridge address in bridgeDeploymentResults.json
COMMON_FOREIGN_BRIDGE_ADDRESS=0x..
# https public RPC node for Foreign network
COMMON_FOREIGN_RPC_URL=https://kovan.infura.io/mew
# public RPC node for Home network
COMMON_HOME_RPC_URL=https://sokol.poa.network
```
* Run the dApp
Using Yarn:
```
yarn start
```
Using Docker:
```
docker-compose up -d
```
The application will run on `http://localhost:PORT`, where `PORT` is specified in your `.env` file.
* Make sure your web3 wallet (Nifty Wallet, AlphaWallet or MetaMask) is funded and connected to the POA Sokol Network (see step 2)
* Specify an amount and click `Transfer` to complete a cross-chain transaction from Sokol to Kovan
### Env Parameter Details
Please refer to [Configuration](../CONFIGURATION.md).
## Testing
To run tests
```
yarn test
```
To run linting
```
yarn lint
```
To run tests with coverage
```
yarn coverage
```
To build the project
```
yarn build
```
## Contributing
See the [CONTRIBUTING](../CONTRIBUTING.md) document for contribution, testing and pull request protocol.
## License
[![License: LGPL v3.0](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
This project is licensed under the GNU Lesser General Public License v3.0. See the [LICENSE](../LICENSE) file for details.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

@ -1,9 +0,0 @@
const { addDecoratorsLegacy, disableEsLint, override } = require('customize-cra')
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
const disableModuleScopePlugin = () => config => {
config.resolve.plugins = config.resolve.plugins.filter(plugin => !(plugin instanceof ModuleScopePlugin))
return config
}
module.exports = override(addDecoratorsLegacy(), disableEsLint(), disableModuleScopePlugin())

@ -1,14 +0,0 @@
---
version: '2.4'
services:
ui:
build:
context: ..
dockerfile: ui/Dockerfile
ports:
- "${UI_PORT}:${UI_PORT}"
env_file: ./.env
environment:
- NODE_ENV=production
restart: unless-stopped
entrypoint: yarn start

@ -1,472 +0,0 @@
/*
This file is part of web3.js.
web3.js is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
web3.js is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file index.js
* @author Fabian Vogelsteller <fabian@ethereum.org>
* @date 2017
*/
"use strict";
var _ = require('underscore');
var core = require('web3-core');
var helpers = require('web3-core-helpers');
var Subscriptions = require('web3-core-subscriptions').subscriptions;
var Method = require('web3-core-method');
var utils = require('web3-utils');
var Net = require('web3-net');
var Personal = require('web3-eth-personal');
var BaseContract = require('web3-eth-contract');
var Iban = require('web3-eth-iban');
var Accounts = require('web3-eth-accounts');
var abi = require('web3-eth-abi');
var getNetworkType = require('./getNetworkType.js');
var formatter = helpers.formatters;
var blockCall = function (args) {
return (_.isString(args[0]) && args[0].indexOf('0x') === 0) ? "eth_getBlockByHash" : "eth_getBlockByNumber";
};
var transactionFromBlockCall = function (args) {
return (_.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getTransactionByBlockHashAndIndex' : 'eth_getTransactionByBlockNumberAndIndex';
};
var uncleCall = function (args) {
return (_.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleByBlockHashAndIndex' : 'eth_getUncleByBlockNumberAndIndex';
};
var getBlockTransactionCountCall = function (args) {
return (_.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getBlockTransactionCountByHash' : 'eth_getBlockTransactionCountByNumber';
};
var uncleCountCall = function (args) {
return (_.isString(args[0]) && args[0].indexOf('0x') === 0) ? 'eth_getUncleCountByBlockHash' : 'eth_getUncleCountByBlockNumber';
};
var Eth = function Eth() {
var _this = this;
// sets _requestmanager
core.packageInit(this, arguments);
// overwrite setProvider
var setProvider = this.setProvider;
this.setProvider = function () {
setProvider.apply(_this, arguments);
_this.net.setProvider.apply(_this, arguments);
_this.personal.setProvider.apply(_this, arguments);
_this.accounts.setProvider.apply(_this, arguments);
_this.Contract.setProvider(_this.currentProvider, _this.accounts);
};
var defaultAccount = null;
var defaultBlock = 'latest';
Object.defineProperty(this, 'defaultAccount', {
get: function () {
return defaultAccount;
},
set: function (val) {
if(val) {
defaultAccount = utils.toChecksumAddress(formatter.inputAddressFormatter(val));
}
// also set on the Contract object
_this.Contract.defaultAccount = defaultAccount;
_this.personal.defaultAccount = defaultAccount;
// update defaultBlock
methods.forEach(function(method) {
method.defaultAccount = defaultAccount;
});
return val;
},
enumerable: true
});
Object.defineProperty(this, 'defaultBlock', {
get: function () {
return defaultBlock;
},
set: function (val) {
defaultBlock = val;
// also set on the Contract object
_this.Contract.defaultBlock = defaultBlock;
_this.personal.defaultBlock = defaultBlock;
// update defaultBlock
methods.forEach(function(method) {
method.defaultBlock = defaultBlock;
});
return val;
},
enumerable: true
});
this.clearSubscriptions = _this._requestManager.clearSubscriptions;
// add net
this.net = new Net(this.currentProvider);
// add chain detection
this.net.getNetworkType = getNetworkType.bind(this);
// add accounts
this.accounts = new Accounts(this.currentProvider);
// add personal
this.personal = new Personal(this.currentProvider);
this.personal.defaultAccount = this.defaultAccount;
// create a proxy Contract type for this instance, as a Contract's provider
// is stored as a class member rather than an instance variable. If we do
// not create this proxy type, changing the provider in one instance of
// web3-eth would subsequently change the provider for _all_ contract
// instances!
var Contract = function Contract() {
BaseContract.apply(this, arguments);
};
Contract.setProvider = function() {
BaseContract.setProvider.apply(this, arguments);
};
// make our proxy Contract inherit from web3-eth-contract so that it has all
// the right functionality and so that instanceof and friends work properly
Contract.prototype = Object.create(BaseContract.prototype);
Contract.prototype.constructor = Contract;
// add contract
this.Contract = Contract;
this.Contract.defaultAccount = this.defaultAccount;
this.Contract.defaultBlock = this.defaultBlock;
this.Contract.setProvider(this.currentProvider, this.accounts);
// add IBAN
this.Iban = Iban;
// add ABI
this.abi = abi;
var methods = [
new Method({
name: 'getProtocolVersion',
call: 'eth_protocolVersion',
params: 0
}),
new Method({
name: 'getCoinbase',
call: 'eth_coinbase',
params: 0
}),
new Method({
name: 'isMining',
call: 'eth_mining',
params: 0
}),
new Method({
name: 'getHashrate',
call: 'eth_hashrate',
params: 0,
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'getChainId',
call: 'eth_chainId',
params: 0,
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'isSyncing',
call: 'eth_syncing',
params: 0,
outputFormatter: formatter.outputSyncingFormatter
}),
new Method({
name: 'getGasPrice',
call: 'eth_gasPrice',
params: 0,
outputFormatter: formatter.outputBigNumberFormatter
}),
new Method({
name: 'getAccounts',
call: 'eth_accounts',
params: 0,
outputFormatter: utils.toChecksumAddress
}),
new Method({
name: 'getBlockNumber',
call: 'eth_blockNumber',
params: 0,
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'getBalance',
call: 'eth_getBalance',
params: 2,
inputFormatter: [formatter.inputAddressFormatter, formatter.inputDefaultBlockNumberFormatter],
outputFormatter: formatter.outputBigNumberFormatter
}),
new Method({
name: 'getStorageAt',
call: 'eth_getStorageAt',
params: 3,
inputFormatter: [formatter.inputAddressFormatter, utils.numberToHex, formatter.inputDefaultBlockNumberFormatter]
}),
new Method({
name: 'getCode',
call: 'eth_getCode',
params: 2,
inputFormatter: [formatter.inputAddressFormatter, formatter.inputDefaultBlockNumberFormatter]
}),
new Method({
name: 'getBlock',
call: blockCall,
params: 2,
inputFormatter: [formatter.inputBlockNumberFormatter, function (val) { return !!val; }],
outputFormatter: formatter.outputBlockFormatter
}),
new Method({
name: 'getUncle',
call: uncleCall,
params: 2,
inputFormatter: [formatter.inputBlockNumberFormatter, utils.numberToHex],
outputFormatter: formatter.outputBlockFormatter,
}),
new Method({
name: 'getBlockTransactionCount',
call: getBlockTransactionCountCall,
params: 1,
inputFormatter: [formatter.inputBlockNumberFormatter],
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'getBlockUncleCount',
call: uncleCountCall,
params: 1,
inputFormatter: [formatter.inputBlockNumberFormatter],
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'getTransaction',
call: 'eth_getTransactionByHash',
params: 1,
inputFormatter: [null],
outputFormatter: formatter.outputTransactionFormatter
}),
new Method({
name: 'getTransactionFromBlock',
call: transactionFromBlockCall,
params: 2,
inputFormatter: [formatter.inputBlockNumberFormatter, utils.numberToHex],
outputFormatter: formatter.outputTransactionFormatter
}),
new Method({
name: 'getTransactionReceipt',
call: 'eth_getTransactionReceipt',
params: 1,
inputFormatter: [null],
outputFormatter: formatter.outputTransactionReceiptFormatter
}),
new Method({
name: 'getTransactionCount',
call: 'eth_getTransactionCount',
params: 2,
inputFormatter: [formatter.inputAddressFormatter, formatter.inputDefaultBlockNumberFormatter],
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'sendSignedTransaction',
call: 'eth_sendRawTransaction',
params: 1,
inputFormatter: [null]
}),
new Method({
name: 'signTransaction',
call: 'eth_signTransaction',
params: 1,
inputFormatter: [formatter.inputTransactionFormatter]
}),
new Method({
name: 'sendTransaction',
call: 'eth_sendTransaction',
params: 1,
inputFormatter: [formatter.inputTransactionFormatter]
}),
new Method({
name: 'sign',
call: 'eth_sign',
params: 2,
inputFormatter: [formatter.inputSignFormatter, formatter.inputAddressFormatter],
transformPayload: function (payload) {
payload.params.reverse();
return payload;
}
}),
new Method({
name: 'call',
call: 'eth_call',
params: 2,
inputFormatter: [formatter.inputCallFormatter, formatter.inputDefaultBlockNumberFormatter]
}),
new Method({
name: 'estimateGas',
call: 'eth_estimateGas',
params: 1,
inputFormatter: [formatter.inputCallFormatter],
outputFormatter: utils.hexToNumber
}),
new Method({
name: 'getCompilers',
call: 'eth_getCompilers',
params: 0
}),
new Method({
name: 'compile.solidity',
call: 'eth_compileSolidity',
params: 1
}),
new Method({
name: 'compile.lll',
call: 'eth_compileLLL',
params: 1
}),
new Method({
name: 'compile.serpent',
call: 'eth_compileSerpent',
params: 1
}),
new Method({
name: 'submitWork',
call: 'eth_submitWork',
params: 3
}),
new Method({
name: 'getWork',
call: 'eth_getWork',
params: 0
}),
new Method({
name: 'getPastLogs',
call: 'eth_getLogs',
params: 1,
inputFormatter: [formatter.inputLogFormatter],
outputFormatter: formatter.outputLogFormatter
}),
// subscriptions
new Subscriptions({
name: 'subscribe',
type: 'eth',
subscriptions: {
'newBlockHeaders': {
// TODO rename on RPC side?
subscriptionName: 'newHeads', // replace subscription with this name
params: 0,
outputFormatter: formatter.outputBlockFormatter
},
'pendingTransactions': {
subscriptionName: 'newPendingTransactions', // replace subscription with this name
params: 0
},
'logs': {
params: 1,
inputFormatter: [formatter.inputLogFormatter],
outputFormatter: formatter.outputLogFormatter,
// DUBLICATE, also in web3-eth-contract
subscriptionHandler: function (output) {
if(output.removed) {
this.emit('changed', output);
} else {
this.emit('data', output);
}
if (_.isFunction(this.callback)) {
this.callback(null, output, this);
}
}
},
'syncing': {
params: 0,
outputFormatter: formatter.outputSyncingFormatter,
subscriptionHandler: function (output) {
var _this = this;
// fire TRUE at start
if(this._isSyncing !== true) {
this._isSyncing = true;
this.emit('changed', _this._isSyncing);
if (_.isFunction(this.callback)) {
this.callback(null, _this._isSyncing, this);
}
setTimeout(function () {
_this.emit('data', output);
if (_.isFunction(_this.callback)) {
_this.callback(null, output, _this);
}
}, 0);
// fire sync status
} else {
this.emit('data', output);
if (_.isFunction(_this.callback)) {
this.callback(null, output, this);
}
// wait for some time before fireing the FALSE
clearTimeout(this._isSyncingTimeout);
this._isSyncingTimeout = setTimeout(function () {
if(output.currentBlock > output.highestBlock - 200) {
_this._isSyncing = false;
_this.emit('changed', _this._isSyncing);
if (_.isFunction(_this.callback)) {
_this.callback(null, _this._isSyncing, _this);
}
}
}, 500);
}
}
}
}
})
];
methods.forEach(function(method) {
method.attachToObject(_this);
method.setRequestManager(_this._requestManager, _this.accounts); // second param means is eth.accounts (necessary for wallet signing)
method.defaultBlock = _this.defaultBlock;
method.defaultAccount = _this.defaultAccount;
});
};
core.addProviders(Eth);
module.exports = Eth;

@ -1,16 +0,0 @@
#!/bin/bash
while read line; do
if [ "$line" = "" ]; then
: # Skip empty lines
elif [[ "$line" =~ \#.* ]]; then
: # Skip comment lines
elif [[ "$line" =~ "UI_PORT"* ]]; then
eval $line
export PORT="$UI_PORT"
else
export "REACT_APP_$line"
fi
done < '.env'
$*

@ -1,54 +0,0 @@
{
"name": "ui",
"version": "0.1.0",
"private": true,
"dependencies": {
"@babel/plugin-proposal-decorators": "^7.4.0",
"bignumber.js": "^6.0.0",
"customize-cra": "^0.2.12",
"date-fns": "^2.13.0",
"dotenv": "^7.0.0",
"fs-extra": "^5.0.0",
"mobx": "^4.0.2",
"mobx-react": "^5.0.0",
"node-sass-chokidar": "^1.0.1",
"numeral": "^2.0.6",
"react": "16.13.1",
"react-app-rewire-mobx": "^1.0.7",
"react-app-rewired": "^2.0.3",
"react-copy-to-clipboard": "^5.0.1",
"react-dom": "16.13.1",
"react-router": "^4.3.1",
"react-router-dom": "^4.2.2",
"react-scripts": "3.0.1",
"react-transition-group": "^2.2.1",
"sweetalert": "^2.1.0",
"web3": "1.0.0-beta.30",
"web3-utils": "1.0.0-beta.30"
},
"scripts": {
"lint": "eslint . --ignore-path ../.eslintignore",
"select-css-theme": "node scripts/selectTheme.js",
"build-css": "node-sass-chokidar src/assets/stylesheets -o src/assets/stylesheets --output-style=compressed -m application*.css",
"watch-css": "nodemon -e scss -x \"yarn build-css\"",
"start": "yarn build-css && yarn select-css-theme && ./load-env.sh react-app-rewired start",
"build": "yarn build-css && yarn select-css-theme && ./load-env.sh react-app-rewired build",
"test": "react-app-rewired test --env=jsdom --no-watch",
"test:watch": "react-app-rewired test --env=jsdom",
"coverage": "react-app-rewired test --env=jsdom --coverage",
"eject": "react-app-rewired eject",
"postinstall": "(cp lib/web3-eth/index.js ../node_modules/web3-eth/src; cp lib/web3-eth/index.js ./node_modules/web3-eth/src) || :"
},
"devDependencies": {
"babel-eslint": "^10.0.1",
"jest-dom": "^3.0.1",
"nodemon": "^1.18.11",
"react-testing-library": "^5.4.4"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

@ -1 +0,0 @@
/* /index.html 200

Some files were not shown because too many files have changed in this diff Show More