Add bridge-ui sub-repository (#27)
* Introduced bridge-ui from branch 2.1.0-rc1 * Added submodule to root directory. * Check out bridge-ui poa-bridge-contracts to tag 2.3.0-rc0 * Added bridge-ui workspace. * Removed duplicate CoC, Contributing, Licence, updated links in readme. * Pulling submodules. * Use specified submodule commit instead of pulling latest.
@ -5,6 +5,7 @@ jobs:
|
||||
- image: circleci/node:10.15
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: yarn
|
||||
- run: yarn run lint
|
||||
test:
|
||||
@ -15,6 +16,7 @@ jobs:
|
||||
FOREIGN_RPC_URL: http://example.com
|
||||
steps:
|
||||
- checkout
|
||||
- run: git submodule update --init
|
||||
- run: yarn
|
||||
- run: yarn run test
|
||||
workflows:
|
||||
|
3
.gitmodules
vendored
@ -1,3 +1,6 @@
|
||||
[submodule "oracle/submodules/poa-bridge-contracts"]
|
||||
path = oracle/submodules/poa-bridge-contracts
|
||||
url = https://github.com/poanetwork/poa-bridge-contracts.git
|
||||
[submodule "bridge-ui/submodules/poa-bridge-contracts"]
|
||||
path = bridge-ui/submodules/poa-bridge-contracts
|
||||
url = https://github.com/poanetwork/poa-bridge-contracts.git
|
||||
|
14
bridge-ui/.babelrc
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": [
|
||||
[
|
||||
"@babel/plugin-proposal-decorators",
|
||||
{
|
||||
"legacy": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
5
bridge-ui/.dockerignore
Normal file
@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
e2e-script
|
||||
.git
|
||||
.env
|
||||
.dockerignore
|
38
bridge-ui/.env.example
Normal file
@ -0,0 +1,38 @@
|
||||
REACT_APP_HOME_BRIDGE_ADDRESS=0xABb4C1399DcC28FBa3Beb76CAE2b50Be3e087353
|
||||
REACT_APP_FOREIGN_BRIDGE_ADDRESS=0xE405F6872cE38a7a4Ff63DcF946236D458c2ca3a
|
||||
REACT_APP_FOREIGN_HTTP_PARITY_URL=https://kovan.infura.io/mew
|
||||
REACT_APP_HOME_HTTP_PARITY_URL=https://sokol.poa.network
|
||||
|
||||
REACT_APP_HOME_NATIVE_NAME=POA
|
||||
|
||||
REACT_APP_HOME_NETWORK_NAME=POA Sokol
|
||||
REACT_APP_FOREIGN_NETWORK_NAME=Kovan
|
||||
|
||||
# Set to true if network doesn't support events
|
||||
REACT_APP_HOME_WITHOUT_EVENTS=false
|
||||
REACT_APP_FOREIGN_WITHOUT_EVENTS=false
|
||||
|
||||
REACT_APP_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx/%s
|
||||
REACT_APP_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
REACT_APP_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
|
||||
REACT_APP_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
|
||||
|
||||
REACT_APP_HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
REACT_APP_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
REACT_APP_HOME_GAS_PRICE_FALLBACK=5000000000
|
||||
REACT_APP_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
|
||||
REACT_APP_FOREIGN_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
REACT_APP_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
REACT_APP_FOREIGN_GAS_PRICE_FALLBACK=5000000000
|
||||
REACT_APP_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
|
||||
# Default
|
||||
REACT_APP_TITLE=TokenBridge UI app - %c
|
||||
REACT_APP_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.
|
||||
|
||||
# RSK
|
||||
#REACT_APP_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
|
||||
#APP_STYLES=classic
|
8
bridge-ui/.eslintrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "react-app",
|
||||
"parserOptions": {
|
||||
"ecmaFeatures": {
|
||||
"legacyDecorators": true
|
||||
}
|
||||
}
|
||||
}
|
29
bridge-ui/.gitignore
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.idea
|
||||
|
||||
# contracts build
|
||||
/src/contracts
|
||||
|
||||
# compiled css
|
||||
*.css
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
1
bridge-ui/.nvmrc
Normal file
@ -0,0 +1 @@
|
||||
v10.15
|
39
bridge-ui/.travis.yml
Normal file
@ -0,0 +1,39 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- "8"
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
dist: trusty
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- google-chrome
|
||||
packages:
|
||||
- google-chrome-stable
|
||||
|
||||
#cache:
|
||||
# directories:
|
||||
# - node_modules
|
||||
|
||||
install:
|
||||
|
||||
before_script:
|
||||
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- sleep 3
|
||||
- wget -N http://chromedriver.storage.googleapis.com/2.30/chromedriver_linux64.zip -P ~/
|
||||
- unzip ~/chromedriver_linux64.zip -d ~/
|
||||
- rm ~/chromedriver_linux64.zip
|
||||
- sudo mv -f ~/chromedriver /usr/local/share/
|
||||
- sudo chmod +x /usr/local/share/chromedriver
|
||||
- sudo ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver
|
||||
|
||||
script:
|
||||
- npm run coverage
|
||||
- cd e2e-script && ./run-tests.sh
|
||||
after_script:
|
||||
- npm run coveralls
|
15
bridge-ui/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
// 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}"
|
||||
}
|
||||
]
|
||||
}
|
8
bridge-ui/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM node:10 as build-deps
|
||||
|
||||
WORKDIR /bridge
|
||||
COPY package.json .
|
||||
COPY package-lock.json .
|
||||
COPY . .
|
||||
RUN npm install
|
||||
RUN npm run postinstall
|
213
bridge-ui/README.md
Normal file
@ -0,0 +1,213 @@
|
||||
# POA Bridge - User Interface (UI) Application
|
||||
|
||||
[![Build Status](https://travis-ci.org/poanetwork/bridge-ui.svg?branch=master)](https://travis-ci.org/poanetwork/bridge-ui)
|
||||
[![Gitter](https://badges.gitter.im/poanetwork/poa-bridge.svg)](https://gitter.im/poanetwork/poa-bridge?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[![Coverage Status](https://coveralls.io/repos/github/poanetwork/bridge-ui/badge.svg?branch=master)](https://coveralls.io/github/poanetwork/bridge-ui?branch=master)
|
||||
[![dependencies Status](https://david-dm.org/poanetwork/bridge-ui/status.svg)](https://david-dm.org/poanetwork/bridge-ui)
|
||||
|
||||
Welcome to the POA Bridge! Following is an overview of the POA Bridge and Bridge UI Application, as well as [basic instructions for getting started](#getting-started).
|
||||
|
||||
## POA Bridge Overview
|
||||
|
||||
The POA Bridge allows users to transfer assets between two chains in the Ethereum ecosystem. It is composed of several elements which are located in different POA Network repositories.
|
||||
|
||||
For a complete picture of the POA Bridge functionality, it is useful to explore each repository.
|
||||
|
||||
**Bridge Elements**
|
||||
1. Bridge UI Application. A DApp interface to transfer tokens and coins between chains, located in this repository.
|
||||
2. [Bridge Smart Contracts](https://github.com/poanetwork/poa-bridge-contracts). Solidity contracts used to manage bridge validators, collect signatures, and confirm asset relay and disposal.
|
||||
3. [Token Bridge](https://github.com/poanetwork/token-bridge). The token bridge oracle written in NodeJS.
|
||||
4. [Bridge Monitor](https://github.com/poanetwork/bridge-monitor). A tool for checking balances and unprocessed events in bridged networks.
|
||||
5. [Bridge Deployment Playbooks](https://github.com/poanetwork/deployment-bridge). Manages configuration instructions for remote deployments and allows you to deploy separate bridge instances for validators.
|
||||
|
||||
## Bridge UI Application
|
||||
|
||||
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.
|
||||
|
||||
The current implementation allows for several bridge modes.
|
||||
|
||||
1. `Native-to-ERC20` Coins on a Home network can be converted to ERC20-compatible tokens on a Foreign network. Coins are locked on the Home side and the corresponding amount of ERC20 tokens are minted on the Foreign side. When the operation is reversed, tokens are burnt on the Foreign side and unlocked in the Home network.
|
||||
2. `ERC20-to-ERC20` ERC20-compatible tokens on the Foreign network are locked and minted as ERC20-compatible tokens (ERC677 tokens) on the Home network. When transferred from Home to Foreign, they are burnt on the Home side and unlocked in the Foreign network. This can be considered a form of atomic swap when a user swaps the token "X" in network "A" to the token "Y" in network "B".
|
||||
3. `ERC20-to-Native` Pre-existing tokens in the Foreign network are locked and coins are minted in the Home network. In this mode, the Home network consensus engine invokes Parity's Block Reward contract to mint coins per the bridge contract request.
|
||||
|
||||
![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.github.io/AlphaWallet-Download-Page/)
|
||||
|
||||
## 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
|
||||
|
||||
- [poa-bridge-contracts](https://github.com/poanetwork/poa-bridge-contracts)
|
||||
- [token-bridge](https://github.com/poanetwork/token-bridge)
|
||||
- [node.js](https://nodejs.org/en/download/)
|
||||
- [AlphaWallet](https://alphawallet.github.io/AlphaWallet-Download-Page/) 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/poa-bridge-contracts`
|
||||
* Follow instructions in the [POA Bridge contracts repo](https://github.com/poanetwork/poa-bridge-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 `poa-bridge-contracts/deploy` directory and includes the bridge contract addresses.
|
||||
|
||||
5. Install and run the POA Token Bridge.
|
||||
* Got to the `sokol-kovan-bridge` folder and `git clone https://github.com/poanetwork/token-bridge`
|
||||
* Follow instructions in the [POA Token Bridge repo](https://github.com/poanetwork/token-bridge).
|
||||
|
||||
If successful, you will see bridge processes run when you issue a command. For example, run `npm run watcher:signature-request`
|
||||
|
||||
**Example NPM 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 unpack this repository.
|
||||
|
||||
* `git clone https://github.com/poanetwork/bridge-ui.git`
|
||||
* `cd bridge-ui`
|
||||
* Update submodules
|
||||
`git submodule update --init --recursive`
|
||||
* Install dependencies
|
||||
`npm install`
|
||||
_**Note**: The bridge UI configuration should be performed with an unprivileged Linux account or with the following flag `npm install --unsafe-perm`. [More information](https://docs.npmjs.com/misc/scripts#user)_
|
||||
* 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 ../poa-bridge-contracts/deploy/bridgeDeploymentResults.json`
|
||||
```bash
|
||||
# HomeBridge address in bridgeDeploymentResults.json
|
||||
REACT_APP_HOME_BRIDGE_ADDRESS=0x..
|
||||
# ForeignBridge address in bridgeDeploymentResults.json
|
||||
REACT_APP_FOREIGN_BRIDGE_ADDRESS=0x..
|
||||
# https public RPC node for Foreign network
|
||||
REACT_APP_FOREIGN_HTTP_PARITY_URL=https://kovan.infura.io/mew
|
||||
# public RPC node for Home network
|
||||
REACT_APP_HOME_HTTP_PARITY_URL=https://sokol.poa.network
|
||||
```
|
||||
|
||||
* Run `npm run start`
|
||||
* 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
|
||||
|
||||
Name | Description
|
||||
--------- | -------
|
||||
REACT_APP_HOME_BRIDGE_ADDRESS | address that you have deployed at step#3. Should also be recorded at `sokol-kovan-bridge/poa-bridge-contracts/deploy/bridgeDeploymentResults.json`
|
||||
REACT_APP_FOREIGN_BRIDGE_ADDRESS | address that you have deployed at step#3.
|
||||
REACT_APP_FOREIGN_HTTP_PARITY_URL | http public rpc node for Foreign Network
|
||||
REACT_APP_HOME_HTTP_PARITY_URL | http public rpc node for Foreign Network
|
||||
REACT_APP_HOME_NATIVE_NAME | name of the home native coin
|
||||
REACT_APP_HOME_NETWORK_NAME | name to be displayed for home network
|
||||
REACT_APP_FOREIGN_NETWORK_NAME | name to be displayed for foreign network
|
||||
REACT_APP_HOME_WITHOUT_EVENTS | `true` if home network doesn't support events
|
||||
REACT_APP_FOREIGN_WITHOUT_EVENTS | `true` if foreign network doesn't support events
|
||||
REACT_APP_HOME_EXPLORER_TX_TEMPLATE | template link to transaction on home explorer. `%s` will be replaced by transaction hash
|
||||
REACT_APP_FOREIGN_EXPLORER_TX_TEMPLATE | template link to transaction on foreign explorer. `%s` will be replaced by transaction hash
|
||||
REACT_APP_HOME_EXPLORER_ADDRESS_TEMPLATE | template link to address on home explorer. `%s` will be replaced by address
|
||||
REACT_APP_FOREIGN_EXPLORER_ADDRESS_TEMPLATE | template link to address on foreign explorer. `%s` will be replaced by address
|
||||
REACT_APP_HOME_GAS_PRICE_ORACLE_URL | The URL used to get a JSON response from the gas price prediction oracle for Home network.
|
||||
REACT_APP_HOME_GAS_PRICE_SPEED_TYPE | Gas Price speed (slow, standard, fast, instant)
|
||||
REACT_APP_HOME_GAS_PRICE_FALLBACK | The gas price (in Wei) that is used if both the oracle and the fall back gas price specified in the Home Bridge contract are not available.
|
||||
REACT_APP_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.
|
||||
REACT_APP_FOREIGN_GAS_PRICE_ORACLE_URL | The URL used to get a JSON response from the gas price prediction oracle for Foreign network.
|
||||
REACT_APP_FOREIGN_GAS_PRICE_SPEED_TYPE | Gas Price speed (slow, standard, fast, instant)
|
||||
REACT_APP_FOREIGN_GAS_PRICE_FALLBACK | The gas price (in Wei) that is used if both the oracle and the fall back gas price specified in the Foreign Bridge contract are not available.
|
||||
REACT_APP_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.
|
||||
REACT_APP_TITLE | The title for the bridge UI page. `%c` will be replaced by the name of the network.
|
||||
REACT_APP_DESCRIPTION | The meta description for the deployed bridge page.
|
||||
APP_STYLES | The set of styles to render the bridge UI page. Currently only `classic` is implemented
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
To run tests
|
||||
|
||||
`npm run test`
|
||||
|
||||
To run tests with coverage
|
||||
|
||||
`npm run coverage`
|
||||
|
||||
## 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.
|
BIN
bridge-ui/bridge-ui.png
Normal file
After Width: | Height: | Size: 106 KiB |
10
bridge-ui/config-overrides.js
Normal file
@ -0,0 +1,10 @@
|
||||
const {
|
||||
addDecoratorsLegacy,
|
||||
disableEsLint,
|
||||
override
|
||||
} = require("customize-cra");
|
||||
|
||||
module.exports = override(
|
||||
addDecoratorsLegacy(),
|
||||
disableEsLint()
|
||||
);
|
BIN
bridge-ui/e2e-script/MetaMask.crx
Normal file
151
bridge-ui/e2e-script/MetaMask.js
Normal file
@ -0,0 +1,151 @@
|
||||
const key = require('selenium-webdriver').Key;
|
||||
const Page = require('./Page.js').Page;
|
||||
const By = require('selenium-webdriver/lib/by').By;
|
||||
const IDMetaMask = "nkbihfbeogaeaoehlefnkodbefgpgknn";
|
||||
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");
|
||||
|
||||
var accountOrderNumber = 1;
|
||||
var networks = [0, 3, 43, 4, 8545];
|
||||
|
||||
class MetaMask extends Page {
|
||||
|
||||
constructor(driver) {
|
||||
super(driver);
|
||||
this.driver = driver;
|
||||
this.URL = URL;
|
||||
}
|
||||
|
||||
async clickButtonSubmitTransaction() {
|
||||
return await 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);
|
||||
let orderNumber = networks.indexOf(provider);
|
||||
let 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 77: {
|
||||
url = "http://10.1.0.102:8545";
|
||||
networks.push(177);
|
||||
break;
|
||||
}
|
||||
case 42: {
|
||||
url = "http://10.1.0.103:8545";
|
||||
networks.push(142);
|
||||
break;
|
||||
}
|
||||
}
|
||||
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: MetaMask
|
||||
};
|
166
bridge-ui/e2e-script/Page.js
Normal file
@ -0,0 +1,166 @@
|
||||
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) {
|
||||
let arr = [];
|
||||
arr[0] = allHandles[0];
|
||||
arr[1] = allHandles[1];
|
||||
allHandles = arr;
|
||||
}
|
||||
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: Page
|
||||
};
|
||||
|
26
bridge-ui/e2e-script/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
### e2e script for bridge-ui
|
||||
Configure startURL, homeAccount, foreignAccount in ```config.json```
|
||||
|
||||
|
||||
#### 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
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
53
bridge-ui/e2e-script/User.js
Normal file
@ -0,0 +1,53 @@
|
||||
const fs = require('fs-extra');
|
||||
const MetaMask = require('./MetaMask.js').MetaMask;
|
||||
const MainPage = require('./mainPage.js').MainPage;
|
||||
|
||||
class User {
|
||||
constructor(driver, file) {
|
||||
try {
|
||||
this.driver = driver;
|
||||
let obj = JSON.parse(fs.readFileSync(file, "utf8"));
|
||||
this.account = obj.account;
|
||||
this.privateKey = obj.privateKey;
|
||||
this.networkID = obj.networkID;
|
||||
this.accountOrderInMetamask = "undefined";//for MetaMaskPage usage only
|
||||
this.name = file;
|
||||
}
|
||||
catch (err) {
|
||||
console.log("instance User was not created");
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
async transferTokens(amount) {
|
||||
let mainPage = new MainPage(this.driver);
|
||||
let 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() {
|
||||
let metaMask = new MetaMask(this.driver);
|
||||
return await metaMask.switchToNextPage() &&
|
||||
await metaMask.setNetwork(this.networkID) &&
|
||||
await metaMask.switchToNextPage();
|
||||
}
|
||||
|
||||
async setMetaMaskAccount() {
|
||||
let metaMask = new MetaMask(this.driver);
|
||||
if (this.accountOrderInMetamask === "undefined") {
|
||||
return await metaMask.importAccount(this);
|
||||
} else
|
||||
return await metaMask.selectAccount(this);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
User: User
|
||||
};
|
68
bridge-ui/e2e-script/Utils.js
Normal file
@ -0,0 +1,68 @@
|
||||
const webdriver = require('selenium-webdriver'),
|
||||
chrome = require('selenium-webdriver/chrome');
|
||||
const fs = require('fs-extra');
|
||||
const configFile = './e2e-script/config.json';
|
||||
|
||||
class Utils {
|
||||
|
||||
static async getHomeAccount() {
|
||||
try {
|
||||
let obj = JSON.parse(fs.readFileSync(configFile), "utf8");
|
||||
return obj.homeAccount;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async getForeignAccount() {
|
||||
try {
|
||||
let obj = JSON.parse(fs.readFileSync(configFile), "utf8");
|
||||
return obj.foreignAccount;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async getStartURL() {
|
||||
try {
|
||||
let obj = JSON.parse(fs.readFileSync(configFile), "utf8");
|
||||
return obj.startUrl;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async getErc20StartURL() {
|
||||
try {
|
||||
let obj = JSON.parse(fs.readFileSync(configFile), "utf8");
|
||||
return obj.erc20Url;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async getErc20NativeStartURL() {
|
||||
try {
|
||||
let obj = JSON.parse(fs.readFileSync(configFile), "utf8");
|
||||
return obj.erc20NativeUrl;
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static async startBrowserWithMetamask() {
|
||||
let source = './e2e-script/MetaMask.crx';
|
||||
let options = new chrome.Options();
|
||||
await options.addExtensions(source);
|
||||
await options.addArguments('disable-popup-blocking');
|
||||
let driver = await new webdriver.Builder().withCapabilities(options.toCapabilities()).build();
|
||||
await driver.sleep(5000);
|
||||
return driver;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Utils: Utils
|
||||
}
|
||||
|
5
bridge-ui/e2e-script/accounts/user42_7FC1.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"account": "0x7FC1442AB55Da569940Eb750AaD2BAA63DA4010E",
|
||||
"privateKey": "460635eb4ac4287de2d2393985e19b4a9f948ac533453a1044ab8d50330b0df9",
|
||||
"networkID" : 42
|
||||
}
|
5
bridge-ui/e2e-script/accounts/user77_7FC1.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"account": "0x7FC1442AB55Da569940Eb750AaD2BAA63DA4010E",
|
||||
"privateKey": "460635eb4ac4287de2d2393985e19b4a9f948ac533453a1044ab8d50330b0df9",
|
||||
"networkID" : 77
|
||||
}
|
14
bridge-ui/e2e-script/bridge/Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
FROM node:8
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y build-essential
|
||||
RUN apt-get install -y libc6-dev
|
||||
RUN apt-get install -y libc6-dev-i386
|
||||
RUN apt-get install -y wget
|
||||
RUN apt-get clean
|
||||
|
||||
RUN git clone https://github.com/poanetwork/bridge-nodejs.git
|
||||
WORKDIR /bridge-nodejs
|
||||
RUN git fetch && git checkout develop
|
||||
RUN cd submodules/poa-bridge-contracts && git submodule update --init --recursive
|
||||
RUN npm install
|
7
bridge-ui/e2e-script/config.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"startUrl" : "http://10.1.0.101:3000",
|
||||
"erc20Url" : "http://10.1.0.104:3000",
|
||||
"erc20NativeUrl" : "http://10.1.0.105:3000",
|
||||
"homeAccount": "./e2e-script/accounts/user77_7FC1.json",
|
||||
"foreignAccount": "./e2e-script/accounts/user42_7FC1.json"
|
||||
}
|
24
bridge-ui/e2e-script/contracts/Dockerfile
Normal file
@ -0,0 +1,24 @@
|
||||
FROM node:8
|
||||
|
||||
RUN mkdir /stuff
|
||||
WORKDIR /stuff
|
||||
|
||||
RUN git clone https://github.com/poanetwork/poa-bridge-contracts.git
|
||||
|
||||
RUN mkdir submodules && \
|
||||
mv poa-bridge-contracts submodules && \
|
||||
cd submodules/poa-bridge-contracts && \
|
||||
git fetch && \
|
||||
git checkout 2.1.0
|
||||
|
||||
RUN cd submodules/poa-bridge-contracts && \
|
||||
npm install && \
|
||||
./node_modules/.bin/truffle compile && \
|
||||
cd deploy && \
|
||||
npm install
|
||||
COPY deploy.sh .
|
||||
COPY contracts.env submodules/poa-bridge-contracts/deploy/
|
||||
COPY erc-contracts.env submodules/poa-bridge-contracts/deploy/
|
||||
COPY erc-native-contracts.env submodules/poa-bridge-contracts/deploy/
|
||||
COPY deployERC20.js submodules/poa-bridge-contracts/deploy/
|
||||
RUN cd submodules/poa-bridge-contracts/deploy && cp contracts.env .env
|
34
bridge-ui/e2e-script/contracts/contracts.env
Normal file
@ -0,0 +1,34 @@
|
||||
BRIDGE_MODE=NATIVE_TO_ERC
|
||||
DEPLOYMENT_ACCOUNT_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
|
||||
DEPLOYMENT_GAS_LIMIT=4000000
|
||||
HOME_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50
|
||||
|
||||
BRIDGEABLE_TOKEN_NAME="Your New Bridged Token"
|
||||
BRIDGEABLE_TOKEN_SYMBOL="TEST"
|
||||
BRIDGEABLE_TOKEN_DECIMALS="18"
|
||||
|
||||
HOME_RPC_URL=http://parity1:8545
|
||||
HOME_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_UPGRADEABLE_ADMIN_VALIDATORS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_UPGRADEABLE_ADMIN_BRIDGE=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_DAILY_LIMIT=30000000000000000000000000
|
||||
HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000
|
||||
HOME_MIN_AMOUNT_PER_TX=10000000000000000
|
||||
HOME_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
HOME_GAS_PRICE=1000000000
|
||||
|
||||
FOREIGN_RPC_URL=http://parity2:8545
|
||||
FOREIGN_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_UPGRADEABLE_ADMIN_VALIDATORS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_UPGRADEABLE_ADMIN_BRIDGE=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_DAILY_LIMIT=15000000000000000000000000
|
||||
FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000
|
||||
FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000
|
||||
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
FOREIGN_GAS_PRICE=10000000000
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
14
bridge-ui/e2e-script/contracts/deploy.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
cd submodules/poa-bridge-contracts/deploy
|
||||
echo "Deploying Native-Erc contracts"
|
||||
node deploy.js
|
||||
echo "Deploying erc20 contract"
|
||||
node deployERC20.js
|
||||
cp erc-contracts.env .env
|
||||
echo "Deploying erc20-erc20 contracts"
|
||||
node deploy.js
|
||||
cp erc-native-contracts.env .env
|
||||
echo "Deploying Block Reward contract"
|
||||
node src/utils/deployBlockReward.js
|
||||
echo "Deploying erc20-native contracts"
|
||||
node deploy.js
|
44
bridge-ui/e2e-script/contracts/deployERC20.js
Normal file
@ -0,0 +1,44 @@
|
||||
/* eslint import/no-unresolved: 0 node/no-missing-require: 0 */
|
||||
const path = require('path')
|
||||
require('dotenv').config();
|
||||
const {
|
||||
deployContract,
|
||||
sendRawTx
|
||||
} = require('./src/deploymentUtils')
|
||||
const {
|
||||
web3Foreign,
|
||||
deploymentPrivateKey
|
||||
} = require('./src/web3')
|
||||
const POA20 = require('../build/contracts/ERC677BridgeToken.json')
|
||||
const user = '0x7FC1442AB55Da569940Eb750AaD2BAA63DA4010E'
|
||||
|
||||
const { DEPLOYMENT_ACCOUNT_ADDRESS } = process.env
|
||||
|
||||
async function deployErc20() {
|
||||
try {
|
||||
let foreignNonce = await web3Foreign.eth.getTransactionCount(DEPLOYMENT_ACCOUNT_ADDRESS)
|
||||
console.log('\n[Foreign] Deploying POA20 Test token')
|
||||
const poa20foreign = await deployContract(POA20, ['POA ERC20 Test', 'POA20', 18], {
|
||||
from: DEPLOYMENT_ACCOUNT_ADDRESS,
|
||||
network: 'foreign',
|
||||
nonce: foreignNonce
|
||||
})
|
||||
foreignNonce++
|
||||
console.log('[Foreign] POA20 Test: ', poa20foreign.options.address)
|
||||
|
||||
const mintData = await poa20foreign.methods
|
||||
.mint(user, '500000000000000000000')
|
||||
.encodeABI({ from: DEPLOYMENT_ACCOUNT_ADDRESS })
|
||||
await sendRawTx({
|
||||
data: mintData,
|
||||
nonce: foreignNonce,
|
||||
to: poa20foreign.options.address,
|
||||
privateKey: deploymentPrivateKey,
|
||||
url: process.env.FOREIGN_RPC_URL
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
|
||||
deployErc20()
|
35
bridge-ui/e2e-script/contracts/erc-contracts.env
Normal file
@ -0,0 +1,35 @@
|
||||
BRIDGE_MODE=ERC_TO_ERC
|
||||
DEPLOYMENT_ACCOUNT_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
|
||||
DEPLOYMENT_GAS_LIMIT=4000000
|
||||
HOME_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50
|
||||
|
||||
BRIDGEABLE_TOKEN_NAME="Your New Bridged Token"
|
||||
BRIDGEABLE_TOKEN_SYMBOL="TEST"
|
||||
BRIDGEABLE_TOKEN_DECIMALS="18"
|
||||
|
||||
HOME_RPC_URL=http://parity1:8545
|
||||
HOME_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_UPGRADEABLE_ADMIN_VALIDATORS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_UPGRADEABLE_ADMIN_BRIDGE=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_DAILY_LIMIT=30000000000000000000000000
|
||||
HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000
|
||||
HOME_MIN_AMOUNT_PER_TX=10000000000000000
|
||||
HOME_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
HOME_GAS_PRICE=1000000000
|
||||
|
||||
FOREIGN_RPC_URL=http://parity2:8545
|
||||
FOREIGN_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_UPGRADEABLE_ADMIN_VALIDATORS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_UPGRADEABLE_ADMIN_BRIDGE=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_DAILY_LIMIT=15000000000000000000000000
|
||||
FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000
|
||||
FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000
|
||||
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
FOREIGN_GAS_PRICE=10000000000
|
||||
ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
36
bridge-ui/e2e-script/contracts/erc-native-contracts.env
Normal file
@ -0,0 +1,36 @@
|
||||
BRIDGE_MODE=ERC_TO_NATIVE
|
||||
DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
|
||||
DEPLOYMENT_GAS_LIMIT=4000000
|
||||
HOME_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000
|
||||
GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50
|
||||
|
||||
BRIDGEABLE_TOKEN_NAME="Your New Bridged Token"
|
||||
BRIDGEABLE_TOKEN_SYMBOL="TEST"
|
||||
BRIDGEABLE_TOKEN_DECIMALS="18"
|
||||
|
||||
HOME_RPC_URL=http://parity1:8545
|
||||
HOME_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_UPGRADEABLE_ADMIN_VALIDATORS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_UPGRADEABLE_ADMIN_BRIDGE=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
HOME_DAILY_LIMIT=30000000000000000000000000
|
||||
HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000
|
||||
HOME_MIN_AMOUNT_PER_TX=10000000000000000
|
||||
HOME_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
HOME_GAS_PRICE=1000000000
|
||||
|
||||
FOREIGN_RPC_URL=http://parity2:8545
|
||||
FOREIGN_OWNER_MULTISIG=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_UPGRADEABLE_ADMIN_VALIDATORS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_UPGRADEABLE_ADMIN_BRIDGE=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
FOREIGN_DAILY_LIMIT=15000000000000000000000000
|
||||
FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000
|
||||
FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000
|
||||
FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1
|
||||
FOREIGN_GAS_PRICE=10000000000
|
||||
|
||||
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
|
||||
ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
213
bridge-ui/e2e-script/docker-compose.yml
Normal file
@ -0,0 +1,213 @@
|
||||
version: '3'
|
||||
services:
|
||||
parity1:
|
||||
build: parity
|
||||
networks:
|
||||
testnet:
|
||||
ipv4_address: 10.1.0.102
|
||||
ports:
|
||||
- "8541:8545"
|
||||
parity2:
|
||||
build:
|
||||
context: parity
|
||||
dockerfile: Dockerfile-foreign
|
||||
networks:
|
||||
testnet:
|
||||
ipv4_address: 10.1.0.103
|
||||
ports:
|
||||
- "8542:8545"
|
||||
contracts:
|
||||
build: contracts
|
||||
networks:
|
||||
- testnet
|
||||
command: "true"
|
||||
redis:
|
||||
image: "redis:4"
|
||||
networks:
|
||||
- testnet
|
||||
rabbit:
|
||||
image: "rabbitmq:3-management"
|
||||
networks:
|
||||
- testnet
|
||||
ports:
|
||||
- "15672:15672"
|
||||
bridge:
|
||||
build: bridge
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- BRIDGE_MODE=NATIVE_TO_ERC
|
||||
- QUEUE_URL=amqp://rabbit
|
||||
- REDIS_URL=redis://redis
|
||||
- HOME_RPC_URL=http://parity1:8545
|
||||
- FOREIGN_RPC_URL=http://parity2:8545
|
||||
- HOME_BRIDGE_ADDRESS=0x32198D570fffC7033641F8A9094FFDCaAEF42624
|
||||
- FOREIGN_BRIDGE_ADDRESS=0x2B6871b9B02F73fa24F4864322CdC78604207769
|
||||
- ERC20_TOKEN_ADDRESS=0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B
|
||||
- VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
- VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
|
||||
- REDIS_LOCK_TTL=1000
|
||||
- HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
- HOME_GAS_PRICE_FALLBACK=1
|
||||
- HOME_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
- FOREIGN_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
- FOREIGN_GAS_PRICE_FALLBACK=1
|
||||
- FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
- HOME_POLLING_INTERVAL=500
|
||||
- FOREIGN_POLLING_INTERVAL=500
|
||||
- ALLOW_HTTP=yes
|
||||
networks:
|
||||
- testnet
|
||||
command: "true"
|
||||
bridge-erc20:
|
||||
build: bridge
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- BRIDGE_MODE=ERC_TO_ERC
|
||||
- QUEUE_URL=amqp://rabbit
|
||||
- REDIS_URL=redis://redis
|
||||
- HOME_RPC_URL=http://parity1:8545
|
||||
- FOREIGN_RPC_URL=http://parity2:8545
|
||||
- HOME_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E
|
||||
- FOREIGN_BRIDGE_ADDRESS=0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127
|
||||
- ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f
|
||||
- BRIDGEABLE_TOKEN_ADDRESS=0x792455a6bCb62Ed4C4362D323E0590654CA4765c
|
||||
- VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
- VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
|
||||
- REDIS_LOCK_TTL=1000
|
||||
- HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
- HOME_GAS_PRICE_FALLBACK=1
|
||||
- HOME_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
- FOREIGN_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
- FOREIGN_GAS_PRICE_FALLBACK=1
|
||||
- FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
- HOME_POLLING_INTERVAL=500
|
||||
- FOREIGN_POLLING_INTERVAL=500
|
||||
- ALLOW_HTTP=yes
|
||||
networks:
|
||||
- testnet
|
||||
command: "true"
|
||||
bridge-erc20-native:
|
||||
build: bridge
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- BRIDGE_MODE=ERC_TO_NATIVE
|
||||
- QUEUE_URL=amqp://rabbit
|
||||
- REDIS_URL=redis://redis
|
||||
- HOME_RPC_URL=http://parity1:8545
|
||||
- FOREIGN_RPC_URL=http://parity2:8545
|
||||
- HOME_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda
|
||||
- FOREIGN_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda
|
||||
- ERC20_TOKEN_ADDRESS=0x3C665A31199694Bf723fD08844AD290207B5797f
|
||||
- BRIDGEABLE_TOKEN_ADDRESS=0x792455a6bCb62Ed4C4362D323E0590654CA4765c
|
||||
- VALIDATOR_ADDRESS=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b
|
||||
- VALIDATOR_ADDRESS_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9
|
||||
- REDIS_LOCK_TTL=1000
|
||||
- HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
- HOME_GAS_PRICE_FALLBACK=1
|
||||
- HOME_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
- FOREIGN_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
- FOREIGN_GAS_PRICE_FALLBACK=1
|
||||
- FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000
|
||||
- HOME_POLLING_INTERVAL=500
|
||||
- FOREIGN_POLLING_INTERVAL=500
|
||||
- ALLOW_HTTP=yes
|
||||
networks:
|
||||
- testnet
|
||||
command: "true"
|
||||
ui:
|
||||
build: ..
|
||||
environment:
|
||||
- REACT_APP_HOME_BRIDGE_ADDRESS=0x32198D570fffC7033641F8A9094FFDCaAEF42624
|
||||
- REACT_APP_FOREIGN_BRIDGE_ADDRESS=0x2B6871b9B02F73fa24F4864322CdC78604207769
|
||||
- REACT_APP_FOREIGN_HTTP_PARITY_URL=http://10.1.0.103:8545
|
||||
- REACT_APP_HOME_HTTP_PARITY_URL=http://10.1.0.102:8545
|
||||
- REACT_APP_HOME_NATIVE_NAME=POA
|
||||
- REACT_APP_HOME_NETWORK_NAME=Sokol
|
||||
- REACT_APP_FOREIGN_NETWORK_NAME=Kovan
|
||||
- REACT_APP_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
|
||||
- REACT_APP_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
- REACT_APP_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
|
||||
- REACT_APP_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
|
||||
- REACT_APP_HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- REACT_APP_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
- REACT_APP_HOME_GAS_PRICE_FALLBACK=5000000000
|
||||
- REACT_APP_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_FALLBACK=5000000000
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
ports:
|
||||
- "3000:3000"
|
||||
networks:
|
||||
testnet:
|
||||
ipv4_address: 10.1.0.101
|
||||
command: "true"
|
||||
ui-erc20:
|
||||
build: ..
|
||||
environment:
|
||||
- REACT_APP_HOME_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E
|
||||
- REACT_APP_FOREIGN_BRIDGE_ADDRESS=0x4a58D6d8D416a5fBCAcf3dC52eb8bE8948E25127
|
||||
- REACT_APP_FOREIGN_HTTP_PARITY_URL=http://10.1.0.103:8545
|
||||
- REACT_APP_HOME_HTTP_PARITY_URL=http://10.1.0.102:8545
|
||||
- REACT_APP_HOME_NATIVE_NAME=POA
|
||||
- REACT_APP_HOME_NETWORK_NAME=Sokol
|
||||
- REACT_APP_FOREIGN_NETWORK_NAME=Kovan
|
||||
- REACT_APP_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
|
||||
- REACT_APP_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
- REACT_APP_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
|
||||
- REACT_APP_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
|
||||
- REACT_APP_HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- REACT_APP_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
- REACT_APP_HOME_GAS_PRICE_FALLBACK=5000000000
|
||||
- REACT_APP_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_FALLBACK=5000000000
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
ports:
|
||||
- "3001:3000"
|
||||
networks:
|
||||
testnet:
|
||||
ipv4_address: 10.1.0.104
|
||||
command: "true"
|
||||
ui-erc20-native:
|
||||
build: ..
|
||||
environment:
|
||||
- REACT_APP_HOME_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda
|
||||
- REACT_APP_FOREIGN_BRIDGE_ADDRESS=0x488Af810997eD1730cB3a3918cD83b3216E6eAda
|
||||
- REACT_APP_FOREIGN_HTTP_PARITY_URL=http://10.1.0.103:8545
|
||||
- REACT_APP_HOME_HTTP_PARITY_URL=http://10.1.0.102:8545
|
||||
- REACT_APP_HOME_NATIVE_NAME=POA
|
||||
- REACT_APP_HOME_NETWORK_NAME=Sokol
|
||||
- REACT_APP_FOREIGN_NETWORK_NAME=Kovan
|
||||
- REACT_APP_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx//%s
|
||||
- REACT_APP_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
- REACT_APP_HOME_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/poa/sokol/address/%s
|
||||
- REACT_APP_FOREIGN_EXPLORER_ADDRESS_TEMPLATE=https://blockscout.com/eth/kovan/address/%s
|
||||
- REACT_APP_HOME_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- REACT_APP_HOME_GAS_PRICE_SPEED_TYPE=standard
|
||||
- REACT_APP_HOME_GAS_PRICE_FALLBACK=5000000000
|
||||
- REACT_APP_HOME_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_ORACLE_URL=https://gasprice.poa.network/
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_SPEED_TYPE=standard
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_FALLBACK=5000000000
|
||||
- REACT_APP_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=15000
|
||||
ports:
|
||||
- "3002:3000"
|
||||
networks:
|
||||
testnet:
|
||||
ipv4_address: 10.1.0.105
|
||||
command: "true"
|
||||
networks:
|
||||
testnet:
|
||||
driver: bridge
|
||||
ipam:
|
||||
driver: default
|
||||
config:
|
||||
- subnet: 10.1.0.0/24
|
133
bridge-ui/e2e-script/mainPage.js
Normal file
@ -0,0 +1,133 @@
|
||||
const Page = require('./Page.js').Page;
|
||||
const By = require('selenium-webdriver/lib/by').By;
|
||||
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 {
|
||||
constructor(driver) {
|
||||
super(driver);
|
||||
this.url;
|
||||
this.fieldHomePOABalance;
|
||||
this.fieldForeignPOABalance;
|
||||
}
|
||||
|
||||
async initFieldsBalance() {
|
||||
if (!(await this.waitUntilWeb3Loaded())) return null;
|
||||
try {
|
||||
let array;
|
||||
array = await super.findWithWait(fieldsBalance);
|
||||
this.fieldHomePOABalance = array[0];
|
||||
this.fieldForeignPOABalance = array[1];
|
||||
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 await this.clickWithWait(buttonTransfer);
|
||||
}
|
||||
|
||||
async clickButtonOk() {
|
||||
return await super.clickWithWait(buttonOk);
|
||||
}
|
||||
|
||||
async clickButtonTransferConfirm() {
|
||||
return await super.clickWithWait(buttonTransferConfirm);
|
||||
}
|
||||
|
||||
async isPresentButtonOk() {
|
||||
return await super.isElementDisplayed(buttonOk, 180);
|
||||
}
|
||||
|
||||
async waitUntilWeb3Loaded() {
|
||||
return await this.waitUntilLocated(classWeb3Loaded, 180);
|
||||
}
|
||||
|
||||
async isPendingTransaction() {
|
||||
return await super.isElementLocated(classPendingTransaction);
|
||||
}
|
||||
|
||||
async waitUntilTransactionDone() {
|
||||
return await this.waitUntilDisappear(classPendingTransaction, 360);
|
||||
}
|
||||
|
||||
async waitUntilShowUpButtonOk() {
|
||||
return await super.waitUntilDisplayed(buttonOk, 360);
|
||||
}
|
||||
|
||||
async waitUntilShowUpButtonTransferConfirm() {
|
||||
return await super.waitUntilDisplayed(buttonTransferConfirm, 360);
|
||||
}
|
||||
|
||||
async waitUntilShowUpButtonOk() {
|
||||
return await super.waitUntilDisplayed(buttonOk, 360);
|
||||
}
|
||||
|
||||
async waitUntilShowUpLoadingContainer() {
|
||||
return await super.waitUntilDisplayed(loadingContainer, 180);
|
||||
}
|
||||
|
||||
async isDisplayedLoadingContainer() {
|
||||
return await super.isElementDisplayed(loadingContainer);
|
||||
}
|
||||
|
||||
async confirmDisclaimer() {
|
||||
return await super.waitUntilDisplayed(disclaimer, 180) &&
|
||||
//await this.clickCheckboxDisclaimer() &&
|
||||
await this.clickButtonDisclaimerConfirm();
|
||||
}
|
||||
|
||||
async clickButtonDisclaimerConfirm() {
|
||||
return await super.clickWithWait(buttonDisclaimerConfirm);
|
||||
}
|
||||
|
||||
async clickCheckboxDisclaimer() {
|
||||
return await 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: MainPage
|
||||
};
|
7
bridge-ui/e2e-script/parity/Dockerfile
Normal file
@ -0,0 +1,7 @@
|
||||
FROM parity/parity:v2.3.3
|
||||
|
||||
WORKDIR /stuff
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["--chain", "chain.json", "--network-id", "77", "--jsonrpc-apis", "all", "--jsonrpc-interface", "all", "--jsonrpc-cors", "all", "--jsonrpc-hosts", "all"]
|
7
bridge-ui/e2e-script/parity/Dockerfile-foreign
Normal file
@ -0,0 +1,7 @@
|
||||
FROM parity/parity:v2.3.3
|
||||
|
||||
WORKDIR /stuff
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["--chain", "chain-foreign.json", "--network-id", "42", "--jsonrpc-apis", "all", "--jsonrpc-interface", "all", "--jsonrpc-cors", "all", "--jsonrpc-hosts", "all"]
|
52
bridge-ui/e2e-script/parity/chain-foreign.json
Normal file
52
bridge-ui/e2e-script/parity/chain.json
Normal file
27
bridge-ui/e2e-script/run-tests.sh
Executable file
@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
docker-compose up -d --build --force-recreate
|
||||
cd ..
|
||||
npm run start:blocks &
|
||||
cd e2e-script
|
||||
docker-compose run contracts ./deploy.sh
|
||||
docker-compose run -d bridge npm run watcher:signature-request
|
||||
docker-compose run -d bridge npm run watcher:collected-signatures
|
||||
docker-compose run -d bridge npm run watcher:affirmation-request
|
||||
docker-compose run -d bridge-erc20 npm run watcher:signature-request
|
||||
docker-compose run -d bridge-erc20 npm run watcher:collected-signatures
|
||||
docker-compose run -d bridge-erc20 npm run watcher:affirmation-request
|
||||
docker-compose run -d bridge-erc20-native npm run watcher:signature-request
|
||||
docker-compose run -d bridge-erc20-native npm run watcher:collected-signatures
|
||||
docker-compose run -d bridge-erc20-native npm run watcher:affirmation-request
|
||||
docker-compose run -d bridge npm run sender:home
|
||||
docker-compose run -d bridge npm run sender:foreign
|
||||
docker-compose run -d ui npm start
|
||||
docker-compose run -d ui-erc20 npm start
|
||||
docker-compose run -d ui-erc20-native npm start
|
||||
cd ..
|
||||
npm run startE2e
|
||||
rc=$?
|
||||
cd e2e-script
|
||||
ps | grep node | grep -v grep | awk '{print "kill " $1}' | sh
|
||||
docker-compose down
|
||||
exit $rc
|
33
bridge-ui/e2e-script/scripts/blocks.js
Normal file
@ -0,0 +1,33 @@
|
||||
const Web3 = require('web3')
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider('http://10.1.0.102:8545'))
|
||||
const foreignWeb3 = new Web3(new Web3.providers.HttpProvider('http://10.1.0.103:8545'))
|
||||
const account = '0x7FC1442AB55Da569940Eb750AaD2BAA63DA4010E'
|
||||
const privateKey = '0x460635eb4ac4287de2d2393985e19b4a9f948ac533453a1044ab8d50330b0df9'
|
||||
homeWeb3.eth.accounts.wallet.add(privateKey)
|
||||
foreignWeb3.eth.accounts.wallet.add(privateKey)
|
||||
|
||||
function generateNewBlock(web3, address) {
|
||||
return web3.eth.sendTransaction({
|
||||
from: address,
|
||||
to: '0x0000000000000000000000000000000000000000',
|
||||
gasPrice: '1',
|
||||
gas: '21000',
|
||||
value: '1'
|
||||
})
|
||||
}
|
||||
|
||||
function main() {
|
||||
setTimeout(async () => {
|
||||
generateNewBlock(homeWeb3, account)
|
||||
generateNewBlock(foreignWeb3, account)
|
||||
main()
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
process.on('SIGTERM', function () {
|
||||
console.log('Finishing sending blocks...')
|
||||
process.exit(0);
|
||||
});
|
309
bridge-ui/e2e-script/test.js
Normal file
@ -0,0 +1,309 @@
|
||||
let test = require('selenium-webdriver/testing');
|
||||
let assert = require('assert');
|
||||
const Utils = require('./Utils.js').Utils;
|
||||
const MetaMask = require('./MetaMask.js').MetaMask;
|
||||
const MainPage = require('./mainPage.js').MainPage;
|
||||
const User = require("./User.js").User;
|
||||
|
||||
test.describe('e2e-test for bridge.poa, version 1.5.0', async function () {
|
||||
this.timeout(5 * 60000);
|
||||
this.slow(1 * 60000);
|
||||
|
||||
const maxAmountPerTransactionLimit = 1;
|
||||
let startURL;
|
||||
let driver;
|
||||
let mainPage;
|
||||
let homeAccount;
|
||||
let foreignAccount;
|
||||
let metaMask;
|
||||
let foreignBalanceBefore;
|
||||
let homeBalanceBefore;
|
||||
|
||||
test.before(async function () {
|
||||
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 function () {
|
||||
try {
|
||||
await driver.quit();
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
});
|
||||
|
||||
test.it('User is able to open main page of bridge-ui ',
|
||||
async function () {
|
||||
startURL = await Utils.getStartURL();
|
||||
let result = await mainPage.open(startURL);
|
||||
console.log("Test URL: " + startURL);
|
||||
return await assert.equal(result, true, "Test FAILED. Build failed.");
|
||||
});
|
||||
|
||||
test.it('Home page: disclaimer is displayed ',
|
||||
async function () {
|
||||
let result = await mainPage.confirmDisclaimer();
|
||||
return await assert.equal(result, true, "Test FAILED. Disclaimer is not displayed");
|
||||
});
|
||||
|
||||
test.it('Main page: foreign POA balance is displayed ',
|
||||
async function () {
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance();
|
||||
console.log("foreignBalanceBefore = "+foreignBalanceBefore);
|
||||
let result = foreignBalanceBefore === 0;
|
||||
return await assert.equal(result, true, "Test FAILED.Foreign POA balance is zero or not displayed ");
|
||||
});
|
||||
|
||||
test.it('Main page: home POA balance is displayed ',
|
||||
async function () {
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance();
|
||||
console.log("homeBalanceBefore = "+homeBalanceBefore);
|
||||
let result = homeBalanceBefore !== 0;
|
||||
return await assert.equal(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 function () {
|
||||
let result = await homeAccount.transferTokens(maxAmountPerTransactionLimit);
|
||||
return await assert.equal(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 function () {
|
||||
let newHomeBalance = await mainPage.getHomePOABalance();
|
||||
let shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit;
|
||||
console.log("newHomeBalance = " + newHomeBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
let result = (Math.abs(shouldBe - newHomeBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
homeBalanceBefore = newHomeBalance;
|
||||
return await assert.equal(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 function () {
|
||||
let newForeignBalance = await mainPage.getForeignPOABalance();
|
||||
|
||||
let shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit;
|
||||
console.log("newForeignBalance = " + newForeignBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
|
||||
let result = (Math.abs(shouldBe - newForeignBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
return await assert.equal(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 function () {
|
||||
await foreignAccount.setMetaMaskNetwork();
|
||||
foreignBalanceBefore = await mainPage.getHomePOABalance();
|
||||
let result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit);
|
||||
return await assert.equal(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 function () {
|
||||
let newForeignBalance = await mainPage.getHomePOABalance();
|
||||
let shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit;
|
||||
console.log("newForeignBalance = " + newForeignBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
let result = (Math.abs(shouldBe - newForeignBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
return await assert.equal(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 function () {
|
||||
let newHomeBalance = await mainPage.getForeignPOABalance();
|
||||
let shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit;
|
||||
console.log("newHomeBalance = " + newHomeBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
let result = (Math.abs(shouldBe - newHomeBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
return await assert.equal(result, true, "Test FAILED.Home POA balance is not correct after transaction");
|
||||
});
|
||||
|
||||
test.it('ERC20-ERC20 - User is able to open main page of bridge-ui ',
|
||||
async function () {
|
||||
startURL = await Utils.getErc20StartURL();
|
||||
let result = await mainPage.open(startURL);
|
||||
console.log("Test URL: " + startURL);
|
||||
return await assert.equal(result, true, "Test FAILED. Build failed.");
|
||||
});
|
||||
|
||||
test.it('ERC20-ERC20 - Home page: disclaimer is displayed ',
|
||||
async function () {
|
||||
let result = await mainPage.confirmDisclaimer();
|
||||
return await assert.equal(result, true, "Test FAILED. Disclaimer is not displayed");
|
||||
});
|
||||
|
||||
test.it('ERC20-ERC20 - Main page: foreign erc20 balance is displayed ',
|
||||
async function () {
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance();
|
||||
console.log("foreignBalanceBefore = "+foreignBalanceBefore);
|
||||
let result = foreignBalanceBefore === 0;
|
||||
return await assert.equal(result, true, "Test FAILED. Foreign erc20 balance is not zero");
|
||||
});
|
||||
|
||||
test.it('ERC20-ERC20 - Main page: home erc20 balance is displayed ',
|
||||
async function () {
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance();
|
||||
console.log("homeBalanceBefore = "+homeBalanceBefore);
|
||||
let result = homeBalanceBefore !== 0;
|
||||
return await assert.equal(result, true, "Test FAILED. Home erc20 balance is zero or not displayed ");
|
||||
});
|
||||
|
||||
test.it('ERC20-ERC20 - User is able to send tokens from Foreign account to Home account ',
|
||||
async function () {
|
||||
homeBalanceBefore = await mainPage.getForeignPOABalance();
|
||||
foreignBalanceBefore = await mainPage.getHomePOABalance();
|
||||
let result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit);
|
||||
return await assert.equal(result, true, "Test FAILED. User is able send tokens from Foreign account to Home account");
|
||||
});
|
||||
|
||||
test.it('ERC20-ERC20 - Foreign POA balance has correctly changed after transaction',
|
||||
async function () {
|
||||
let newForeignBalance = await mainPage.getHomePOABalance();
|
||||
let shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit;
|
||||
console.log("newForeignBalance = " + newForeignBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
let result = (Math.abs(shouldBe - newForeignBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
return await assert.equal(result, true, "Test FAILED.Foreign POA balance is not correct after transaction");
|
||||
});
|
||||
|
||||
test.it('ERC20-ERC20 - Home account has received correct amount of tokens after transaction ',
|
||||
async function () {
|
||||
let newHomeBalance = await mainPage.getForeignPOABalance();
|
||||
let shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit;
|
||||
console.log("newHomeBalance = " + newHomeBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
let result = (Math.abs(shouldBe - newHomeBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
return await assert.equal(result, true, "Test FAILED.Home POA balance is not correct after transaction");
|
||||
});
|
||||
test.it('ERC20-ERC20 - User is able to send tokens from Home account to Foreign account ',
|
||||
async function () {
|
||||
await homeAccount.setMetaMaskNetwork();
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance();
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance();
|
||||
let result = await homeAccount.transferTokens(maxAmountPerTransactionLimit);
|
||||
return await assert.equal(result, true, "Test FAILED. User is able send tokens from Home account to Foreign account");
|
||||
});
|
||||
|
||||
test.it('ERC20-ERC20 - Home POA balance has correctly changed after transaction',
|
||||
async function () {
|
||||
let newHomeBalance = await mainPage.getHomePOABalance();
|
||||
let shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit;
|
||||
console.log("newHomeBalance = " + newHomeBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
let result = (Math.abs(shouldBe - newHomeBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
homeBalanceBefore = newHomeBalance;
|
||||
return await assert.equal(result, true, "Test FAILED.Home POA balance is not correct after transaction");
|
||||
});
|
||||
|
||||
test.it('ERC20-ERC20 - Foreign account has received correct amount of tokens after transaction ',
|
||||
async function () {
|
||||
let newForeignBalance = await mainPage.getForeignPOABalance();
|
||||
|
||||
let shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit;
|
||||
console.log("newForeignBalance = " + newForeignBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
|
||||
let result = (Math.abs(shouldBe - newForeignBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
return await assert.equal(result, true, "Test FAILED. Foreign POA balance is not correct after transaction");
|
||||
});
|
||||
|
||||
test.it('ERC20-Native - User is able to open main page of bridge-ui ',
|
||||
async function () {
|
||||
startURL = await Utils.getErc20NativeStartURL();
|
||||
let result = await mainPage.open(startURL);
|
||||
console.log("Test URL: " + startURL);
|
||||
return await assert.equal(result, true, "Test FAILED. Build failed.");
|
||||
});
|
||||
|
||||
test.it('ERC20-Native - Home page: disclaimer is displayed ',
|
||||
async function () {
|
||||
let result = await mainPage.confirmDisclaimer();
|
||||
return await assert.equal(result, true, "Test FAILED. Disclaimer is not displayed");
|
||||
});
|
||||
|
||||
test.it('ERC20-Native - Main page: foreign erc20 balance is displayed ',
|
||||
async function () {
|
||||
await foreignAccount.setMetaMaskNetwork();
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance();
|
||||
console.log("foreignBalanceBefore = "+foreignBalanceBefore);
|
||||
let result = foreignBalanceBefore !== 0;
|
||||
return await assert.equal(result, true, "Test FAILED. Foreign erc20 balance is zero");
|
||||
});
|
||||
|
||||
test.it('ERC20-Native - Main page: home erc20 balance is displayed ',
|
||||
async function () {
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance();
|
||||
console.log("homeBalanceBefore = "+homeBalanceBefore);
|
||||
let result = homeBalanceBefore !== 0;
|
||||
return await assert.equal(result, true, "Test FAILED. Home erc20 balance is zero or not displayed ");
|
||||
});
|
||||
|
||||
test.it('ERC20-Native - User is able to send tokens from Foreign account to Home account ',
|
||||
async function () {
|
||||
homeBalanceBefore = await mainPage.getForeignPOABalance();
|
||||
foreignBalanceBefore = await mainPage.getHomePOABalance();
|
||||
let result = await foreignAccount.transferTokens(maxAmountPerTransactionLimit);
|
||||
return await assert.equal(result, true, "Test FAILED. User is able send tokens from Foreign account to Home account");
|
||||
});
|
||||
|
||||
test.it('ERC20-Native - Foreign POA balance has correctly changed after transaction',
|
||||
async function () {
|
||||
let newForeignBalance = await mainPage.getHomePOABalance();
|
||||
let shouldBe = foreignBalanceBefore - maxAmountPerTransactionLimit;
|
||||
console.log("newForeignBalance = " + newForeignBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
let result = (Math.abs(shouldBe - newForeignBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
return await assert.equal(result, true, "Test FAILED.Foreign POA balance is not correct after transaction");
|
||||
});
|
||||
|
||||
test.it('ERC20-Native - Home account has received correct amount of tokens after transaction ',
|
||||
async function () {
|
||||
let newHomeBalance = await mainPage.getForeignPOABalance();
|
||||
let shouldBe = homeBalanceBefore + maxAmountPerTransactionLimit;
|
||||
console.log("newHomeBalance = " + newHomeBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
let result = (Math.abs(shouldBe - newHomeBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
return await assert.equal(result, true, "Test FAILED.Home POA balance is not correct after transaction");
|
||||
});
|
||||
test.it('ERC20-Native - User is able to send tokens from Home account to Foreign account ',
|
||||
async function () {
|
||||
await homeAccount.setMetaMaskNetwork();
|
||||
homeBalanceBefore = await mainPage.getHomePOABalance();
|
||||
foreignBalanceBefore = await mainPage.getForeignPOABalance();
|
||||
let result = await homeAccount.transferTokens(maxAmountPerTransactionLimit);
|
||||
return await assert.equal(result, true, "Test FAILED. User is able send tokens from Home account to Foreign account");
|
||||
});
|
||||
|
||||
test.it('ERC20-Native - Home POA balance has correctly changed after transaction',
|
||||
async function () {
|
||||
let newHomeBalance = await mainPage.getHomePOABalance();
|
||||
let shouldBe = homeBalanceBefore - maxAmountPerTransactionLimit;
|
||||
console.log("newHomeBalance = " + newHomeBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
let result = (Math.abs(shouldBe - newHomeBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
homeBalanceBefore = newHomeBalance;
|
||||
return await assert.equal(result, true, "Test FAILED.Home POA balance is not correct after transaction");
|
||||
});
|
||||
|
||||
test.it('ERC20-Native - Foreign account has received correct amount of tokens after transaction ',
|
||||
async function () {
|
||||
let newForeignBalance = await mainPage.getForeignPOABalance();
|
||||
|
||||
let shouldBe = foreignBalanceBefore + maxAmountPerTransactionLimit;
|
||||
console.log("newForeignBalance = " + newForeignBalance);
|
||||
console.log("shouldBe = " + shouldBe);
|
||||
|
||||
let result = (Math.abs(shouldBe - newForeignBalance)) < (maxAmountPerTransactionLimit / 100);
|
||||
return await assert.equal(result, true, "Test FAILED. Foreign POA balance is not correct after transaction");
|
||||
});
|
||||
});
|
472
bridge-ui/lib/web3-eth/index.js
Normal file
@ -0,0 +1,472 @@
|
||||
/*
|
||||
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;
|
18159
bridge-ui/package-lock.json
generated
Normal file
62
bridge-ui/package.json
Normal file
@ -0,0 +1,62 @@
|
||||
{
|
||||
"name": "bridge-ui",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "https://poanetwork.github.io/",
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-decorators": "^7.4.0",
|
||||
"bignumber.js": "^6.0.0",
|
||||
"chromedriver": "^2.35.0",
|
||||
"coveralls": "^3.0.0",
|
||||
"customize-cra": "^0.2.12",
|
||||
"dotenv": "^7.0.0",
|
||||
"fs-extra": "^5.0.0",
|
||||
"gh-pages": "^1.1.0",
|
||||
"mobx": "^4.0.2",
|
||||
"mobx-react": "^5.0.0",
|
||||
"mocha": "^5.1.1",
|
||||
"node-sass-chokidar": "^1.0.1",
|
||||
"numeral": "^2.0.6",
|
||||
"react": "^16.2.0",
|
||||
"react-app-rewire-mobx": "^1.0.7",
|
||||
"react-app-rewired": "^2.0.3",
|
||||
"react-copy-to-clipboard": "^5.0.1",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-router": "^4.3.1",
|
||||
"react-router-dom": "^4.2.2",
|
||||
"react-scripts": "2.1.8",
|
||||
"react-transition-group": "^2.2.1",
|
||||
"selenium-webdriver": "^3.6.0",
|
||||
"sweetalert": "^2.1.0",
|
||||
"web3": "1.0.0-beta.30",
|
||||
"web3-utils": "1.0.0-beta.30"
|
||||
},
|
||||
"scripts": {
|
||||
"select-css-theme": "node scripts/selectTheme.js",
|
||||
"build-css": "node-sass-chokidar src/assets/stylesheets -o src/assets/stylesheets --output-style=compressed -m application*.css",
|
||||
"watch-css": "nodemon -e scss -x \"npm run build-css\"",
|
||||
"start": "npm run build-css && npm run select-css-theme && react-app-rewired start",
|
||||
"build": "npm run compile:contracts && npm run build-css && npm run select-css-theme && react-app-rewired build",
|
||||
"test": "react-app-rewired test --env=jsdom",
|
||||
"startE2e": "mocha -b ./e2e-script/test.js",
|
||||
"start:blocks": "node ./e2e-script/scripts/blocks.js",
|
||||
"coverage": "react-app-rewired test --env=jsdom --coverage",
|
||||
"coveralls": "cat ./coverage/lcov.info | node node_modules/.bin/coveralls",
|
||||
"eject": "react-app-rewired eject",
|
||||
"predeploy": "npm run build",
|
||||
"deploy": "gh-pages -d build -o origin",
|
||||
"compile:contracts": "cd submodules/poa-bridge-contracts && npm install && npm run compile && cd ../../ && rm -r -f src/contracts && cp -r submodules/poa-bridge-contracts/build/contracts src/contracts",
|
||||
"postinstall": "cp lib/web3-eth/index.js ../node_modules/web3-eth/src && npm run compile:contracts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"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
bridge-ui/public/_redirects
Normal file
@ -0,0 +1 @@
|
||||
/* /index.html 200
|
BIN
bridge-ui/public/favicon.ico
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
bridge-ui/public/favicons/android-chrome-192x192.png
Executable file
After Width: | Height: | Size: 4.7 KiB |
BIN
bridge-ui/public/favicons/android-chrome-256x256.png
Executable file
After Width: | Height: | Size: 6.3 KiB |
BIN
bridge-ui/public/favicons/apple-touch-icon.png
Executable file
After Width: | Height: | Size: 4.4 KiB |
9
bridge-ui/public/favicons/browserconfig.xml
Executable file
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
BIN
bridge-ui/public/favicons/fav_2.png
Executable file
After Width: | Height: | Size: 9.5 KiB |
BIN
bridge-ui/public/favicons/favicon-16x16.png
Executable file
After Width: | Height: | Size: 1.3 KiB |
BIN
bridge-ui/public/favicons/favicon-32x32.png
Normal file
After Width: | Height: | Size: 921 B |
BIN
bridge-ui/public/favicons/favicon.ico
Normal file
After Width: | Height: | Size: 7.7 KiB |
18
bridge-ui/public/favicons/manifest.json
Executable file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
BIN
bridge-ui/public/favicons/mstile-150x150.png
Executable file
After Width: | Height: | Size: 8.7 KiB |
BIN
bridge-ui/public/images/bridgeogimage.jpg
Normal file
After Width: | Height: | Size: 144 KiB |
25
bridge-ui/public/index.html
Normal file
@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta property="og:title" content="POA Bridge UI" />
|
||||
<meta property="og:description" content="%REACT_APP_DESCRIPTION%" />
|
||||
<meta property="og:url" content="https://poanetwork.github.io/bridge-ui" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:image" content="/images/bridgeogimage.jpg">
|
||||
<link href="https://fonts.googleapis.com/css?family=Nunito:300,400,700" rel="stylesheet">
|
||||
<link rel="manifest" href="%PUBLIC_URL%/favicons/manifest.json">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicons/favicon.ico">
|
||||
<title>TokenBridge UI app</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
15
bridge-ui/public/manifest.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "POA Bridge UI",
|
||||
"name": "POA Bridge UI",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
BIN
bridge-ui/public/x_chain_bridge.jpeg
Normal file
After Width: | Height: | Size: 32 KiB |
17
bridge-ui/scripts/selectTheme.js
Normal file
@ -0,0 +1,17 @@
|
||||
const path = require('path');
|
||||
require('dotenv').config({
|
||||
path: path.resolve(__dirname, '..', '.env')
|
||||
});
|
||||
const fs = require('fs');
|
||||
|
||||
const stylePath = path.resolve(__dirname, '..', 'src', 'assets', 'stylesheets');
|
||||
const destinationFilename = 'application.css';
|
||||
let filename;
|
||||
|
||||
if (process.env.APP_STYLES === 'classic') {
|
||||
filename = 'application.classic.css'
|
||||
} else {
|
||||
filename = 'application.core.css'
|
||||
}
|
||||
|
||||
fs.copyFileSync(path.resolve(stylePath, filename), path.resolve(stylePath, destinationFilename));
|
62
bridge-ui/src/App.js
Normal file
@ -0,0 +1,62 @@
|
||||
import React from 'react';
|
||||
import { Header, Bridge, RelayEvents, Footer, SweetAlert, Loading, StatusPage, StatisticsPage } from './components';
|
||||
import { Route } from 'react-router-dom'
|
||||
import './assets/stylesheets/application.css';
|
||||
import { Disclaimer } from './components'
|
||||
import { ModalContainer } from './components'
|
||||
import { NoWallet } from './components'
|
||||
import { setItem, getItem, DISCLAIMER_KEY } from './components/utils/localstorage'
|
||||
|
||||
export class App extends React.Component {
|
||||
state = {
|
||||
showDisclaimer: false,
|
||||
showMobileMenu: false
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const disclaimerDisplayed = getItem(DISCLAIMER_KEY)
|
||||
|
||||
if(!disclaimerDisplayed) {
|
||||
this.setState({ showDisclaimer: true })
|
||||
}
|
||||
}
|
||||
|
||||
closeDisclaimer = () => {
|
||||
setItem(DISCLAIMER_KEY, true)
|
||||
this.setState({showDisclaimer: false})
|
||||
}
|
||||
|
||||
toggleMobileMenu = () => {
|
||||
this.setState(prevState => ({ showMobileMenu: !prevState.showMobileMenu}))
|
||||
}
|
||||
|
||||
render() {
|
||||
const { showDisclaimer, showMobileMenu } = this.state
|
||||
return (
|
||||
<div className={showMobileMenu ? 'mobile-menu-is-open' : ''}>
|
||||
<Route component={Loading}/>
|
||||
<Route component={SweetAlert}/>
|
||||
<Route render={() =>
|
||||
<Header
|
||||
onMenuToggle={this.toggleMobileMenu}
|
||||
showMobileMenu={showMobileMenu}
|
||||
/>
|
||||
}/>
|
||||
<div className="app-container">
|
||||
{showMobileMenu && <Route render={() => <div className="mobile-menu-open"/>}/>}
|
||||
<Route exact path="/" component={Bridge}/>
|
||||
<Route exact path="/events" component={RelayEvents}/>
|
||||
<Route exact path="/status" component={StatusPage}/>
|
||||
<Route exact path="/statistics" component={StatisticsPage}/>
|
||||
</div>
|
||||
<Route component={Footer}/>
|
||||
<ModalContainer
|
||||
showModal={showDisclaimer}
|
||||
>
|
||||
<Disclaimer onConfirmation={this.closeDisclaimer} />
|
||||
</ModalContainer>
|
||||
<NoWallet showModal={!showDisclaimer} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
0
bridge-ui/src/assets/images/.gitkeep
Normal file
BIN
bridge-ui/src/assets/images/disclaimer-modal/disclaimer@2x.png
Normal file
After Width: | Height: | Size: 89 KiB |
BIN
bridge-ui/src/assets/images/logos/logo-ethereum-purple.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
bridge-ui/src/assets/images/logos/logo-ethereum.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
27
bridge-ui/src/assets/images/logos/logo-loader.svg
Normal file
@ -0,0 +1,27 @@
|
||||
<svg
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421"
|
||||
version="1.1"
|
||||
viewBox="0 0 330 71"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
@import url('https://fonts.googleapis.com/css?family=Nunito:400');
|
||||
text {
|
||||
font-family: "Nunito", sans-serif;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<text
|
||||
fill="#fff"
|
||||
font-family="Nunito"
|
||||
font-size="26.923px"
|
||||
font-weight="500"
|
||||
x="128.126px"
|
||||
y="44.219px"
|
||||
>Bridge Loading</text>
|
||||
<path
|
||||
d="M25.076,44.801l0,0.001l-1.563,0l0,4.625l-0.003,0l0.003,0.015c0,0.854 -0.713,1.547 -1.593,1.547l-12.625,0c-0.88,0 -1.594,-0.693 -1.594,-1.547l0.003,-0.015l-0.003,0l0,-27.876l0.003,0l-0.003,-0.015c0,-0.854 0.714,-1.547 1.594,-1.547l15.781,0l0,0.001c6.974,0.008 12.625,5.559 12.625,12.405c0,6.847 -5.651,12.397 -12.625,12.406Zm31.125,-24.812c8.56,0 15.5,6.94 15.5,15.5c0,8.56 -6.94,15.5 -15.5,15.5c-8.56,0 -15.5,-6.94 -15.5,-15.5c0,-8.56 6.94,-15.5 15.5,-15.5Zm50.506,30.028c0,0 0,0.001 0,0.002c0,0.523 -0.43,0.954 -0.954,0.955l-35.039,0c-0.026,0.002 -0.049,0.015 -0.076,0.015c-0.518,-0.005 -0.941,-0.435 -0.937,-0.953c0.004,-0.202 0.073,-0.398 0.197,-0.557l-0.018,-0.035l17.364,-28.683l0.039,0c0.084,-0.439 0.469,-0.76 0.915,-0.764c0.459,0 0.825,0.331 0.916,0.764l0.038,0l17.555,28.874l-0.06,0.084c0.036,0.095 0.057,0.196 0.06,0.298Z"
|
||||
fill="#fff"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
BIN
bridge-ui/src/assets/images/logos/logo-poa-20-purple.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
bridge-ui/src/assets/images/logos/logo-poa-20-purple@2x.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
bridge-ui/src/assets/images/logos/logo-poa-20.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
bridge-ui/src/assets/images/logos/logo-poa-20@2x.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
3
bridge-ui/src/assets/images/logos/logo-poa-20_small.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="34" height="8">
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M3.781 6.375h-.344v1.031a.333.333 0 0 1-.312.337v.007H.469v-.007c-.006.001-.01.007-.016.007a.336.336 0 0 1-.328-.344V1.219c0-.19.147-.344.328-.344.006 0 .01.006.016.007V.875h3.312c1.467 0 2.656 1.231 2.656 2.75s-1.189 2.75-2.656 2.75zM10.422.875c1.838 0 3.328 1.539 3.328 3.437 0 1.899-1.49 3.438-3.328 3.438-1.838 0-3.328-1.539-3.328-3.438 0-1.898 1.49-3.437 3.328-3.437zM21.369 7.532a.209.209 0 0 1-.205.212h-7.542c-.005.001-.008.006-.013.006-.112 0-.203-.098-.203-.219 0-.042.023-.076.042-.109l-.008-.017 3.739-6.363h.017c.021-.094.091-.17.188-.17.098 0 .167.076.189.17h.017l3.779 6.405-.021.032c.005.019.021.032.021.053zM33.562 7.75h-4.374a.313.313 0 0 1-.313-.313V1.188c0-.173.14-.313.313-.313h4.374c.173 0 .313.14.313.313v6.249c0 .173-.14.313-.313.313zM29.5 1.5v5.625h3.75V1.5H29.5zm-2.188 5.625a.313.313 0 1 1 0 .625h-4.374a.313.313 0 0 1-.313-.313V4.312c0-.172.14-.312.313-.312H27V1.5h-4.062a.313.313 0 1 1 0-.625h4.374c.173 0 .313.14.313.313v3.124c0 .173-.14.313-.313.313H23.25v2.5h4.062z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
BIN
bridge-ui/src/assets/images/logos/logo-poa-sokol-purple@2x.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
bridge-ui/src/assets/images/logos/logo-poa-sokol.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
bridge-ui/src/assets/images/logos/logo-poa-sokol@2x.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
3
bridge-ui/src/assets/images/modal/close.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="15">
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M8.941 7.468l5.781 5.781a1.019 1.019 0 1 1-1.441 1.441L7.5 8.909 1.719 14.69a1.019 1.019 0 1 1-1.441-1.441l5.781-5.781L.31 1.719A1.019 1.019 0 1 1 1.751.278L7.5 6.027 13.249.278a1.019 1.019 0 1 1 1.441 1.441L8.941 7.468z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 340 B |
BIN
bridge-ui/src/assets/images/no-wallet-modal/i@3x.png
Normal file
After Width: | Height: | Size: 12 KiB |
@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="360">
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M120 300h60v60h-60v-60z"/>
|
||||
<path fill="#5B33A2" fill-rule="evenodd" d="M120 360c-33.137 0-60-26.863-60-60h60v60z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M60 240h60v60H60v-60z"/>
|
||||
<path fill="#5B33A2" fill-rule="evenodd" d="M120 240H60v-60c33.137 0 60 26.863 60 60z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M60 240c-33.137 0-60-26.863-60-60h60v60z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M0 180c0-33.137 26.863-60 60-60v60H0z"/>
|
||||
<path fill="#5B33A2" fill-rule="evenodd" d="M60 180v-60h60c0 33.137-26.863 60-60 60z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M60 60h60v60H60V60z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M60 60c0-33.137 26.863-60 60-60v60H60z"/>
|
||||
<path fill="#5B33A2" fill-rule="evenodd" d="M120 0h60v60h-60V0z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M300 300h60v60h-60v-60z"/>
|
||||
<path fill="#5B33A2" fill-rule="evenodd" d="M300 360c-33.137 0-60-26.863-60-60h60v60z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M240 240h60v60h-60v-60z"/>
|
||||
<path fill="#5B33A2" fill-rule="evenodd" d="M300 240h-60v-60c33.137 0 60 26.863 60 60z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M240 240c-33.137 0-60-26.863-60-60h60v60z"/>
|
||||
<path fill="#5B33A2" fill-rule="evenodd" d="M180 180c0-33.137 26.863-60 60-60v60h-60z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M240 180v-60h60c0 33.137-26.863 60-60 60z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M240 60h60v60h-60V60z"/>
|
||||
<path fill="#5B33A2" fill-rule="evenodd" d="M240 60c0-33.137 26.863-60 60-60v60h-60z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M300 0h60v60h-60V0z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 6.5 KiB |
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="203" height="276">
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M203 0h-51l-50 138 50 138h51l-50-138L203 0z" opacity=".039"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M152 0h-51L51 138l50 138h51l-50-138L152 0z" opacity=".078"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M101 0H50L0 138l50 138h51L51 138 101 0z" opacity=".122"/>
|
||||
</svg>
|
After Width: | Height: | Size: 389 B |
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="203" height="276">
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M0 0h51l50 138-50 138H0l50-138L0 0z" opacity=".039"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M51 0h51l50 138-50 138H51l50-138L51 0z" opacity=".078"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M102 0h51l50 138-50 138h-51l50-138L102 0z" opacity=".122"/>
|
||||
</svg>
|
After Width: | Height: | Size: 379 B |
BIN
bridge-ui/src/assets/images/themes/core/bridge/pattern-1.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
BIN
bridge-ui/src/assets/images/themes/core/bridge/pattern-2.png
Normal file
After Width: | Height: | Size: 6.6 KiB |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="34" height="8">
|
||||
<path fill="#5c34a2" fill-rule="evenodd" d="M3.781 6.375h-.344v1.031a.333.333 0 0 1-.312.337v.007H.469v-.007c-.006.001-.01.007-.016.007a.336.336 0 0 1-.328-.344V1.219c0-.19.147-.344.328-.344.006 0 .01.006.016.007V.875h3.312c1.467 0 2.656 1.231 2.656 2.75s-1.189 2.75-2.656 2.75zM10.422.875c1.838 0 3.328 1.539 3.328 3.437 0 1.899-1.49 3.438-3.328 3.438-1.838 0-3.328-1.539-3.328-3.438 0-1.898 1.49-3.437 3.328-3.437zM21.369 7.532a.209.209 0 0 1-.205.212h-7.542c-.005.001-.008.006-.013.006-.112 0-.203-.098-.203-.219 0-.042.023-.076.042-.109l-.008-.017 3.739-6.363h.017c.021-.094.091-.17.188-.17.098 0 .167.076.189.17h.017l3.779 6.405-.021.032c.005.019.021.032.021.053zM33.562 7.75h-4.374a.313.313 0 0 1-.313-.313V1.188c0-.173.14-.313.313-.313h4.374c.173 0 .313.14.313.313v6.249c0 .173-.14.313-.313.313zM29.5 1.5v5.625h3.75V1.5H29.5zm-2.188 5.625a.313.313 0 1 1 0 .625h-4.374a.313.313 0 0 1-.313-.313V4.312c0-.172.14-.312.313-.312H27V1.5h-4.062a.313.313 0 1 1 0-.625h4.374c.173 0 .313.14.313.313v3.124c0 .173-.14.313-.313.313H23.25v2.5h4.062z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="34" height="8">
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M3.781 6.375h-.344v1.031a.333.333 0 0 1-.312.337v.007H.469v-.007c-.006.001-.01.007-.016.007a.336.336 0 0 1-.328-.344V1.219c0-.19.147-.344.328-.344.006 0 .01.006.016.007V.875h3.312c1.467 0 2.656 1.231 2.656 2.75s-1.189 2.75-2.656 2.75zM10.422.875c1.838 0 3.328 1.539 3.328 3.437 0 1.899-1.49 3.438-3.328 3.438-1.838 0-3.328-1.539-3.328-3.438 0-1.898 1.49-3.437 3.328-3.437zM21.369 7.532a.209.209 0 0 1-.205.212h-7.542c-.005.001-.008.006-.013.006-.112 0-.203-.098-.203-.219 0-.042.023-.076.042-.109l-.008-.017 3.739-6.363h.017c.021-.094.091-.17.188-.17.098 0 .167.076.189.17h.017l3.779 6.405-.021.032c.005.019.021.032.021.053zM33.562 7.75h-4.374a.313.313 0 0 1-.313-.313V1.188c0-.173.14-.313.313-.313h4.374c.173 0 .313.14.313.313v6.249c0 .173-.14.313-.313.313zM29.5 1.5v5.625h3.75V1.5H29.5zm-2.188 5.625a.313.313 0 1 1 0 .625h-4.374a.313.313 0 0 1-.313-.313V4.312c0-.172.14-.312.313-.312H27V1.5h-4.062a.313.313 0 1 1 0-.625h4.374c.173 0 .313.14.313.313v3.124c0 .173-.14.313-.313.313H23.25v2.5h4.062z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.4 KiB |
BIN
bridge-ui/src/assets/images/themes/core/logos/logo-home.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
@ -0,0 +1,27 @@
|
||||
<svg
|
||||
height="26"
|
||||
width="188"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
@import url('https://fonts.googleapis.com/css?family=Nunito:400');
|
||||
text {
|
||||
font-family: "Nunito", sans-serif;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
d="M8.938 17.812h-.813v2.376h-.003l.003.015c0 .44-.364.797-.812.797h-6.5A.805.805 0 0 1 0 20.203l.003-.015H0V5.812h.003L0 5.797C0 5.357.364 5 .813 5h8.125c3.589 0 6.499 2.868 6.499 6.406s-2.91 6.406-6.499 6.406zM25.188 5c4.487 0 8.125 3.582 8.125 8s-3.638 8-8.125 8c-4.488 0-8.126-3.582-8.126-8s3.638-8 8.126-8zM52 20.493a.498.498 0 0 1-.503.494H33.064c-.022.003-.041.013-.064.013a.5.5 0 0 1-.5-.5c0-.109.042-.204.101-.286l-.009-.017 9.151-14.809h.02a.493.493 0 0 1 .965 0h.021L52 20.296l-.032.043c.017.05.032.1.032.154z"
|
||||
fill-rule="evenodd"
|
||||
fill="#FFF"
|
||||
/>
|
||||
<text
|
||||
fill="#fff"
|
||||
font-family="Nunito"
|
||||
font-size="14"
|
||||
font-weight="300"
|
||||
x="70"
|
||||
y="17"
|
||||
>Bridge UI App</text>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
32
bridge-ui/src/assets/images/themes/core/logos/logo.svg
Normal file
@ -0,0 +1,32 @@
|
||||
<svg
|
||||
height="26"
|
||||
width="188"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<defs>
|
||||
<style type="text/css">
|
||||
@import url('https://fonts.googleapis.com/css?family=Nunito:400');
|
||||
text {
|
||||
font-family: "Nunito", sans-serif;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
d="M8.938 17.812h-.813v2.376h-.003l.003.015c0 .44-.364.797-.812.797h-6.5A.805.805 0 0 1 0 20.203l.003-.015H0V5.812h.003L0 5.797C0 5.357.364 5 .813 5h8.125c3.589 0 6.499 2.868 6.499 6.406s-2.91 6.406-6.499 6.406zM25.188 5c4.487 0 8.125 3.582 8.125 8s-3.638 8-8.125 8c-4.488 0-8.126-3.582-8.126-8s3.638-8 8.126-8zM52 20.493a.498.498 0 0 1-.503.494H33.064c-.022.003-.041.013-.064.013a.5.5 0 0 1-.5-.5c0-.109.042-.204.101-.286l-.009-.017 9.151-14.809h.02a.493.493 0 0 1 .965 0h.021L52 20.296l-.032.043c.017.05.032.1.032.154z"
|
||||
fill-rule="evenodd"
|
||||
fill="#FFF"
|
||||
/>
|
||||
<text
|
||||
fill="#fff"
|
||||
font-family="Nunito"
|
||||
font-size="14"
|
||||
font-weight="300"
|
||||
x="70"
|
||||
y="17"
|
||||
>Bridge UI App</text>
|
||||
<path
|
||||
d="M179 0a1 1 0 0 1 1 1v24a1 1 0 0 1-2 0V1a1 1 0 0 1 1-1z"
|
||||
fill-rule="evenodd"
|
||||
fill="#60DB97"
|
||||
/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="59" height="59">
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M6 0h47a6 6 0 0 1 6 6v47a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M19 12h6v6h-6v-6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M13 18a6 6 0 0 1 6-6v6h-6z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M13 18h6v6h-6v-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M13 30v-6h6a6 6 0 0 1-6 6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M7 30a6 6 0 0 1 6-6v6H7z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M13 36a6 6 0 0 1-6-6h6v6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M19 36h-6v-6a6 6 0 0 1 6 6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M13 36h6v6h-6v-6z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M19 48a6 6 0 0 1-6-6h6v6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M19 42h6v6h-6v-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M35 42h6v6h-6v-6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M41 48v-6h6a6 6 0 0 1-6 6z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M41 36h6v6h-6v-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M41 36a6 6 0 0 1 6-6v6h-6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M47 36v-6h6a6 6 0 0 1-6 6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M53 30h-6v-6a6 6 0 0 1 6 6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M47 30a6 6 0 0 1-6-6h6v6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M41 18h6v6h-6v-6z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M47 18h-6v-6a6 6 0 0 1 6 6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M35 12h6v6h-6v-6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="59" height="59">
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M6 0h47a6 6 0 0 1 6 6v47a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M19 12h6v6h-6v-6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M13 18a6 6 0 0 1 6-6v6h-6z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M13 18h6v6h-6v-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M13 30v-6h6a6 6 0 0 1-6 6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M7 30a6 6 0 0 1 6-6v6H7z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M13 36a6 6 0 0 1-6-6h6v6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M19 36h-6v-6a6 6 0 0 1 6 6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M13 36h6v6h-6v-6z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M19 48a6 6 0 0 1-6-6h6v6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M19 42h6v6h-6v-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M35 42h6v6h-6v-6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M41 48v-6h6a6 6 0 0 1-6 6z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M41 36h6v6h-6v-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M41 36a6 6 0 0 1 6-6v6h-6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M47 36v-6h6a6 6 0 0 1-6 6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M53 30h-6v-6a6 6 0 0 1 6 6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M47 30a6 6 0 0 1-6-6h6v6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M41 18h6v6h-6v-6z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M47 18h-6v-6a6 6 0 0 1 6 6z"/>
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M35 12h6v6h-6v-6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="59" height="59">
|
||||
<path fill="#9987FC" fill-rule="evenodd" d="M6 0h47a6 6 0 0 1 6 6v47a6 6 0 0 1-6 6H6a6 6 0 0 1-6-6V6a6 6 0 0 1 6-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M19 12h6v6h-6v-6z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M13 18a6 6 0 0 1 6-6v6h-6z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M13 18h6v6h-6v-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M13 30v-6h6a6 6 0 0 1-6 6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M7 30a6 6 0 0 1 6-6v6H7z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M13 36a6 6 0 0 1-6-6h6v6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M19 36h-6v-6a6 6 0 0 1 6 6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M13 36h6v6h-6v-6z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M19 48a6 6 0 0 1-6-6h6v6z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M19 42h6v6h-6v-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M35 42h6v6h-6v-6z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M41 48v-6h6a6 6 0 0 1-6 6z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M41 36h6v6h-6v-6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M41 36a6 6 0 0 1 6-6v6h-6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M47 36v-6h6a6 6 0 0 1-6 6z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M53 30h-6v-6a6 6 0 0 1 6 6z"/>
|
||||
<path fill="#90E1D9" fill-rule="evenodd" d="M47 30a6 6 0 0 1-6-6h6v6z"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M41 18h6v6h-6v-6z"/>
|
||||
<path fill="#61DC97" fill-rule="evenodd" d="M47 18h-6v-6a6 6 0 0 1 6 6z"/>
|
||||
<path fill="#5C34A2" fill-rule="evenodd" d="M35 12h6v6h-6v-6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="360" height="360">
|
||||
<path fill="#343053" fill-rule="evenodd" d="M120 300h60v60h-60v-60z"/>
|
||||
<path fill="#453E6A" fill-rule="evenodd" d="M120 360c-33.137 0-60-26.863-60-60h60v60z"/>
|
||||
<path fill="#6D649B" fill-rule="evenodd" d="M60 240h60v60H60v-60z"/>
|
||||
<path fill="#453E6A" fill-rule="evenodd" d="M120 240H60v-60c33.137 0 60 26.863 60 60z"/>
|
||||
<path fill="#6D649B" fill-rule="evenodd" d="M60 240c-33.137 0-60-26.863-60-60h60v60z"/>
|
||||
<path fill="#343053" fill-rule="evenodd" d="M0 180c0-33.137 26.863-60 60-60v60H0z"/>
|
||||
<path fill="#6D649B" fill-rule="evenodd" d="M60 180v-60h60c0 33.137-26.863 60-60 60z"/>
|
||||
<path fill="#453E6A" fill-rule="evenodd" d="M60 60h60v60H60V60z"/>
|
||||
<path fill="#343053" fill-rule="evenodd" d="M60 60c0-33.137 26.863-60 60-60v60H60z"/>
|
||||
<path fill="#6D649B" fill-rule="evenodd" d="M120 0h60v60h-60V0z"/>
|
||||
<path fill="#343053" fill-rule="evenodd" d="M300 300h60v60h-60v-60z"/>
|
||||
<path fill="#453E6A" fill-rule="evenodd" d="M300 360c-33.137 0-60-26.863-60-60h60v60z"/>
|
||||
<path fill="#6D649B" fill-rule="evenodd" d="M240 240h60v60h-60v-60z"/>
|
||||
<path fill="#453E6A" fill-rule="evenodd" d="M300 240h-60v-60c33.137 0 60 26.863 60 60z"/>
|
||||
<path fill="#6D649B" fill-rule="evenodd" d="M240 240c-33.137 0-60-26.863-60-60h60v60z"/>
|
||||
<path fill="#343053" fill-rule="evenodd" d="M180 180c0-33.137 26.863-60 60-60v60h-60z"/>
|
||||
<path fill="#6D649B" fill-rule="evenodd" d="M240 180v-60h60c0 33.137-26.863 60-60 60z"/>
|
||||
<path fill="#453E6A" fill-rule="evenodd" d="M240 60h60v60h-60V60z"/>
|
||||
<path fill="#6D649B" fill-rule="evenodd" d="M240 60c0-33.137 26.863-60 60-60v60h-60z"/>
|
||||
<path fill="#343053" fill-rule="evenodd" d="M300 0h60v60h-60V0z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
BIN
bridge-ui/src/assets/images/themes/ethereum-classic/bridge/center-image-pointing-down@2x.png
Normal file
After Width: | Height: | Size: 6.5 KiB |
5
bridge-ui/src/assets/images/themes/ethereum-classic/bridge/center-image-pointing-left.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="203" height="276">
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M203 0h-51l-50 138 50 138h51l-50-138L203 0z" opacity=".039"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M152 0h-51L51 138l50 138h51l-50-138L152 0z" opacity=".078"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M101 0H50L0 138l50 138h51L51 138 101 0z" opacity=".122"/>
|
||||
</svg>
|
After Width: | Height: | Size: 389 B |
5
bridge-ui/src/assets/images/themes/ethereum-classic/bridge/center-image-pointing-right.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="203" height="276">
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M0 0h51l50 138-50 138H0l50-138L0 0z" opacity=".039"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M51 0h51l50 138-50 138H51l50-138L51 0z" opacity=".078"/>
|
||||
<path fill="#FFF" fill-rule="evenodd" d="M102 0h51l50 138-50 138h-51l50-138L102 0z" opacity=".122"/>
|
||||
</svg>
|
After Width: | Height: | Size: 379 B |
BIN
bridge-ui/src/assets/images/themes/ethereum-classic/bridge/pattern-1.png
Executable file
After Width: | Height: | Size: 11 KiB |
BIN
bridge-ui/src/assets/images/themes/ethereum-classic/bridge/pattern-2.png
Executable file
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="38">
|
||||
<path fill="#807E8E" fill-rule="evenodd" d="M12 14L0 19.5 12 0v14z"/>
|
||||
<path fill="#3C3264" fill-rule="evenodd" d="M12 14L0 19.5 12 26V14z"/>
|
||||
<path fill="#807E8E" fill-rule="evenodd" d="M12 29L0 22l12 16v-9z"/>
|
||||
<path fill="#352D59" fill-rule="evenodd" d="M12 14l12 5.5L12 0v14z"/>
|
||||
<path fill="#1D1830" fill-rule="evenodd" d="M12 14l12 5.5L12 26V14z"/>
|
||||
<path fill="#352D59" fill-rule="evenodd" d="M12 29l12-7-12 16v-9z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 515 B |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="25" height="39">
|
||||
<path fill="#352D59" fill-rule="evenodd" d="M24.987 19.514l-.004.02c.012.467-.173.909-.516 1.223l-10.56 17.45a1.622 1.622 0 0 1-.535.521c-.042.03-.08.062-.126.088-.043.023-.092.023-.136.042a1.455 1.455 0 0 1-.61.124 1.455 1.455 0 0 1-.61-.124c-.044-.019-.093-.018-.136-.042-.046-.026-.084-.059-.126-.088a1.622 1.622 0 0 1-.535-.521L.533 20.757a1.592 1.592 0 0 1-.516-1.223l-.004-.02c.001-.007-.003-.014-.003-.021l.003-.018c.003-.056.008-.11.017-.166.028-.402.195-.777.491-1.055L11.093.786c.136-.225.327-.39.537-.523.042-.029.079-.062.124-.087.043-.023.091-.022.135-.041.105-.043.207-.078.318-.099a1.596 1.596 0 0 1 .904.099c.044.019.092.018.135.041.045.025.082.058.124.087.209.133.401.298.537.523l10.572 17.468c.296.278.462.653.491 1.055.009.056.014.109.017.166.001.005.003.011.003.017 0 .008-.004.015-.003.022zM12.5 34.636l6.202-10.247-5.242 3.243a1.394 1.394 0 0 1-.729.328c-.078.015-.153.015-.231.017-.078-.002-.153-.002-.231-.017a1.394 1.394 0 0 1-.729-.328l-5.242-3.243L12.5 34.636zm0-30.28l-6.21 10.26 5.25-3.248c.211-.186.462-.288.729-.328.078-.015.153-.015.231-.017.078.002.153.002.231.017.267.04.518.142.729.328l5.25 3.248-6.21-10.26zm0 10.065L4.289 19.5l8.211 5.079 8.211-5.079-8.211-5.079z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 12 KiB |