Merge the develop branch to the master branch, preparation to v2.1.0
This commit is contained in:
commit
4117f29a74
@ -2,3 +2,4 @@ node_modules
|
||||
submodules
|
||||
coverage
|
||||
lib
|
||||
dist
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,6 +6,7 @@ coverage
|
||||
|
||||
# production
|
||||
build
|
||||
dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
@ -48,4 +49,4 @@ __pycache__
|
||||
|
||||
#monitor
|
||||
monitor/responses/*
|
||||
!monitor/.gitkeep
|
||||
!monitor/.gitkeep
|
||||
|
@ -70,3 +70,4 @@ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT | Average gas usage of a transaction sent by
|
||||
MONITOR_TX_NUMBER_THRESHOLD | If estimated number of transaction is equal to or below this value, the monitor will report that the validator has less funds than it is required. | integer
|
||||
MONITOR_PORT | The port for the Monitor. | integer
|
||||
MONITOR_BRIDGE_NAME | The name to be used in the url path for the bridge | string
|
||||
MONITOR_CACHE_EVENTS | If set to true, monitor will cache obtained events for other workers runs
|
||||
|
31
burner-wallet-plugin/.eslintrc.js
Normal file
31
burner-wallet-plugin/.eslintrc.js
Normal file
@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
parser: "@typescript-eslint/parser", // Specifies the ESLint parser
|
||||
extends: [
|
||||
"plugin:react/recommended",
|
||||
"plugin:@typescript-eslint/recommended", // Uses the recommended rules from @typescript-eslint/eslint-plugin
|
||||
"../.eslintrc"
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: "module", // Allows for the use of imports
|
||||
ecmaFeatures: {
|
||||
jsx: true // Allows for the parsing of JSX
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off", // Reduce the use of 'any'
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"react/prop-types": "off",
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/member-delimiter-style": "off",
|
||||
"@typescript-eslint/indent": "off",
|
||||
"@typescript-eslint/explicit-member-accessibility": "off"
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: "detect",
|
||||
}
|
||||
}
|
||||
};
|
30
burner-wallet-plugin/Dockerfile
Normal file
30
burner-wallet-plugin/Dockerfile
Normal file
@ -0,0 +1,30 @@
|
||||
FROM node:12 as plugin-base
|
||||
|
||||
WORKDIR /mono
|
||||
COPY package.json .
|
||||
RUN mkdir -p contracts/node_modules
|
||||
|
||||
COPY burner-wallet-plugin/package.json ./burner-wallet-plugin/
|
||||
COPY burner-wallet-plugin/lerna.json ./burner-wallet-plugin/
|
||||
COPY burner-wallet-plugin/yarn.lock ./burner-wallet-plugin/
|
||||
COPY burner-wallet-plugin/tsconfig.json ./burner-wallet-plugin/
|
||||
COPY burner-wallet-plugin/tokenbridge-bw-exchange/package.json ./burner-wallet-plugin/tokenbridge-bw-exchange/
|
||||
COPY burner-wallet-plugin/staging/package.json ./burner-wallet-plugin/staging/
|
||||
COPY burner-wallet-plugin/testing/package.json ./burner-wallet-plugin/testing/
|
||||
COPY yarn.lock .
|
||||
RUN yarn install --production --frozen-lockfile
|
||||
|
||||
COPY ./burner-wallet-plugin/tokenbridge-bw-exchange ./burner-wallet-plugin/tokenbridge-bw-exchange
|
||||
RUN yarn build:plugin
|
||||
|
||||
|
||||
FROM plugin-base as testing
|
||||
COPY ./burner-wallet-plugin/testing ./burner-wallet-plugin/testing
|
||||
WORKDIR /mono/burner-wallet-plugin
|
||||
CMD ["yarn", "start-testing"]
|
||||
|
||||
|
||||
FROM plugin-base as staging
|
||||
COPY ./burner-wallet-plugin/staging ./burner-wallet-plugin/staging
|
||||
WORKDIR /mono/burner-wallet-plugin
|
||||
CMD ["yarn", "start-staging"]
|
41
burner-wallet-plugin/README.md
Normal file
41
burner-wallet-plugin/README.md
Normal file
@ -0,0 +1,41 @@
|
||||
# TokenBridge Burner Wallet 2 Plugin
|
||||
|
||||
Please refer to the [Plugin README](./tokenrbdige-bw-exchange/README.md) for resources provided, instructions to install and use the plugin.
|
||||
|
||||
### Setup
|
||||
1. [Initialize](../README.md#initializing-the-monorepository) the monorepository.
|
||||
2. Run `yarn build` or from the monorepository root `yarn build:plugin`
|
||||
|
||||
### Run Burner Wallet with the plugin in Mainnet & Classic
|
||||
1. Create `.env` file in `staging` folder and set `REACT_APP_INFURA_KEY=<your key from infura.com>`
|
||||
2. Run `yarn start-staging` to start the wallet connected to Mainnet & Classic and interact with the ETH - WETC Bridge.
|
||||
|
||||
### Run Burner Wallet with the plugin in Sokol & Kovan
|
||||
1. Create `.env` file in `testing` folder and set `REACT_APP_INFURA_KEY=<your key from infura.com>`.
|
||||
Also, a private key can be set to start the wallet with the specified account `REACT_APP_PK=0x...`
|
||||
2. Run `yarn start-testing` to start the wallet connected to Sokol & Kovan and interact with a test bridge
|
||||
that works on top of the AMB bridge.
|
||||
|
||||
### Docker Setup
|
||||
Docker can be used to build the services and run the testing and staging wallets.
|
||||
|
||||
First you may want to create the `.env` files for testing and staging as mentioned before. This is optional before building the containers, variables can be passes later using `--env-file` or `--env` parameters in `docker run`.
|
||||
|
||||
Build the services with docker-compose:
|
||||
```bash
|
||||
docker-compose build
|
||||
```
|
||||
|
||||
### Run Burner Wallet with the plugin in Mainnet & Classic using Docker
|
||||
```bash
|
||||
docker run -ti -p 8080:8080 -e PORT=8080 --rm burner-wallet-plugin_staging
|
||||
```
|
||||
|
||||
### Run Burner Wallet with the plugin in Sokol & Kovan using Docker
|
||||
```bash
|
||||
docker run -ti -p 8080:8080 -e PORT=8080 --rm burner-wallet-plugin_testing
|
||||
```
|
||||
### Publish to npm
|
||||
In order to make this plugin accessible, it should be available as a npm package. Follow the [instructions](publish.md) to publish
|
||||
the package to npm registry.
|
||||
|
17
burner-wallet-plugin/docker-compose.yml
Normal file
17
burner-wallet-plugin/docker-compose.yml
Normal file
@ -0,0 +1,17 @@
|
||||
---
|
||||
version: '2.4'
|
||||
services:
|
||||
staging:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: burner-wallet-plugin/Dockerfile
|
||||
target: staging
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
testing:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: burner-wallet-plugin/Dockerfile
|
||||
target: testing
|
||||
environment:
|
||||
- NODE_ENV=production
|
10
burner-wallet-plugin/lerna.json
Normal file
10
burner-wallet-plugin/lerna.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"packages": [
|
||||
"basic-wallet",
|
||||
"local-wallet",
|
||||
"tokenbridge-bw-exchange"
|
||||
],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "independent"
|
||||
}
|
28
burner-wallet-plugin/package.json
Normal file
28
burner-wallet-plugin/package.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "burner-wallet-plugin",
|
||||
"description": "Burner Wallet 2 plugin",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-only",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"install": "lerna bootstrap",
|
||||
"build": "lerna run --ignore testing --ignore staging build --stream",
|
||||
"lint": "eslint '*/**/*.{js,ts,tsx}' --ignore-path ../.eslintignore",
|
||||
"start-staging": "lerna run --scope staging start --stream",
|
||||
"start-testing": "lerna run --scope testing start --stream",
|
||||
"test": "lerna run --ignore testing --ignore staging test --stream"
|
||||
},
|
||||
"workspaces": [
|
||||
"staging",
|
||||
"testing",
|
||||
"tokenbridge-bw-exchange"
|
||||
],
|
||||
"dependencies": {
|
||||
"@types/color": "3.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "1.13.0",
|
||||
"@typescript-eslint/parser": "1.13.0",
|
||||
"eslint-plugin-react": "7.19.0",
|
||||
"lerna": "3.16.4",
|
||||
"typescript": "3.5.3"
|
||||
}
|
||||
}
|
36
burner-wallet-plugin/publish.md
Normal file
36
burner-wallet-plugin/publish.md
Normal file
@ -0,0 +1,36 @@
|
||||
## Plugin Package Information
|
||||
|
||||
The package to be published gets its configuration from `tokenbridge/burner-wallet-plugin/tokenbridge-bw-exchange/package.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "tokenbridge-bw-exchange",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"/dist"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- `name` is the name of how package will be available in npm.
|
||||
- `main` is entry point for the package
|
||||
- `types` is the entry point for typescript types
|
||||
- `files` is the list of files included when publishing the package. So we have to run `yarn build` first to
|
||||
generate the `dist` folder.
|
||||
|
||||
## Steps to publish to npm
|
||||
|
||||
1. Create account in https://www.npmjs.com/
|
||||
|
||||
2. Go to `tokenbridge/burner-wallet-plugin/tokenbridge-bw-exchange/`
|
||||
|
||||
3. Run `yarn build`. Make sure it generates the `dist` folder
|
||||
|
||||
4. Update `version` in `tokenbridge/burner-wallet-plugin/tokenbridge-bw-exchange/package.json`
|
||||
5. Run `yarn login` and fill login information if required.
|
||||
6. Run `yarn publish --access public`.
|
||||
The prompt will ask for the new version, complete it with the version from `package.json`
|
||||
|
||||
More information in https://classic.yarnpkg.com/en/docs/publishing-a-package/
|
1
burner-wallet-plugin/staging/.env.example
Normal file
1
burner-wallet-plugin/staging/.env.example
Normal file
@ -0,0 +1 @@
|
||||
REACT_APP_INFURA_KEY=
|
40
burner-wallet-plugin/staging/package.json
Normal file
40
burner-wallet-plugin/staging/package.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "staging",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@burner-wallet/assets": "^1.1.10",
|
||||
"@burner-wallet/core": "^1.1.0",
|
||||
"@burner-wallet/exchange": "^1.1.4",
|
||||
"@burner-wallet/metamask-plugin": "^1.0.0",
|
||||
"@burner-wallet/modern-ui": "^1.0.7",
|
||||
"@poanet/tokenbridge-bw-exchange": "^1.0.0",
|
||||
"@types/node": "12.0.4",
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "16.8.4",
|
||||
"@types/react-router-dom": "^4.3.3",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"typescript": "3.5.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {}
|
||||
}
|
BIN
burner-wallet-plugin/staging/public/favicon.ico
Normal file
BIN
burner-wallet-plugin/staging/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
38
burner-wallet-plugin/staging/public/index.html
Normal file
38
burner-wallet-plugin/staging/public/index.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
15
burner-wallet-plugin/staging/public/manifest.json
Normal file
15
burner-wallet-plugin/staging/public/manifest.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
21
burner-wallet-plugin/staging/src/index.tsx
Normal file
21
burner-wallet-plugin/staging/src/index.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import BurnerCore from '@burner-wallet/core'
|
||||
import { InjectedSigner, LocalSigner } from '@burner-wallet/core/signers'
|
||||
import { InfuraGateway, InjectedGateway } from '@burner-wallet/core/gateways'
|
||||
import Exchange from '@burner-wallet/exchange'
|
||||
import ModernUI from '@burner-wallet/modern-ui'
|
||||
import { Etc, Wetc, TokenBridgeGateway, WETCBridge } from '@poanet/tokenbridge-bw-exchange'
|
||||
import MetamaskPlugin from '@burner-wallet/metamask-plugin'
|
||||
|
||||
const core = new BurnerCore({
|
||||
signers: [new InjectedSigner(), new LocalSigner()],
|
||||
gateways: [new InjectedGateway(), new InfuraGateway(process.env.REACT_APP_INFURA_KEY), new TokenBridgeGateway()],
|
||||
assets: [Wetc, Etc]
|
||||
})
|
||||
|
||||
const exchange = new Exchange([new WETCBridge()])
|
||||
|
||||
const BurnerWallet = () => <ModernUI title="Staging Wallet" core={core} plugins={[exchange, new MetamaskPlugin()]} />
|
||||
|
||||
ReactDOM.render(<BurnerWallet />, document.getElementById('root'))
|
1
burner-wallet-plugin/staging/src/react-app-env.d.ts
vendored
Normal file
1
burner-wallet-plugin/staging/src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
25
burner-wallet-plugin/staging/tsconfig.json
Normal file
25
burner-wallet-plugin/staging/tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
14
burner-wallet-plugin/testing/.env.example
Normal file
14
burner-wallet-plugin/testing/.env.example
Normal file
@ -0,0 +1,14 @@
|
||||
REACT_APP_INFURA_KEY=
|
||||
#REACT_APP_PK=0x
|
||||
|
||||
REACT_APP_MODE=AMB_NATIVE_TO_ERC677
|
||||
|
||||
REACT_APP_HOME_TOKEN_NAME=sPOA
|
||||
REACT_APP_HOME_NETWORK=77
|
||||
REACT_APP_HOME_MEDIATOR_ADDRESS=0x867949C3F2f66D827Ed40847FaA7B3a369370e13
|
||||
REACT_APP_HOME_TOKEN_ADDRESS=
|
||||
|
||||
REACT_APP_FOREIGN_TOKEN_NAME=ksPOA
|
||||
REACT_APP_FOREIGN_NETWORK=42
|
||||
REACT_APP_FOREIGN_MEDIATOR_ADDRESS=0x99FB1a25caeB9c3a5Bf132686E2fe5e27BC0e2dd
|
||||
REACT_APP_FOREIGN_TOKEN_ADDRESS=0xff94183659f549D6273349696d73686Ee1d2AC83
|
43
burner-wallet-plugin/testing/package.json
Normal file
43
burner-wallet-plugin/testing/package.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "testing",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@burner-wallet/assets": "^1.1.10",
|
||||
"@burner-wallet/core": "^1.1.0",
|
||||
"@burner-wallet/exchange": "^1.1.4",
|
||||
"@burner-wallet/metamask-plugin": "^1.0.0",
|
||||
"@burner-wallet/modern-ui": "^1.0.7",
|
||||
"@poanet/tokenbridge-bw-exchange": "^1.0.0",
|
||||
"@types/node": "12.0.4",
|
||||
"@types/react": "16.8.19",
|
||||
"@types/react-dom": "16.8.4",
|
||||
"@types/react-router-dom": "^4.3.3",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"typescript": "3.5.1",
|
||||
"web3": "1.0.0-beta.55"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^0.19.0"
|
||||
}
|
||||
}
|
BIN
burner-wallet-plugin/testing/public/favicon.ico
Normal file
BIN
burner-wallet-plugin/testing/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
38
burner-wallet-plugin/testing/public/index.html
Normal file
38
burner-wallet-plugin/testing/public/index.html
Normal file
@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
15
burner-wallet-plugin/testing/public/manifest.json
Normal file
15
burner-wallet-plugin/testing/public/manifest.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "Burner Wallet",
|
||||
"name": "Burner Wallet",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
73
burner-wallet-plugin/testing/src/index.tsx
Normal file
73
burner-wallet-plugin/testing/src/index.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Asset } from '@burner-wallet/assets'
|
||||
import BurnerCore from '@burner-wallet/core'
|
||||
import { InjectedSigner, LocalSigner } from '@burner-wallet/core/signers'
|
||||
import { InfuraGateway, InjectedGateway } from '@burner-wallet/core/gateways'
|
||||
import ModernUI from '@burner-wallet/modern-ui'
|
||||
import Exchange from '@burner-wallet/exchange'
|
||||
import { Mediator, sPOA, ERC677Asset, TokenBridgeGateway } from '@poanet/tokenbridge-bw-exchange'
|
||||
import MetamaskPlugin from '@burner-wallet/metamask-plugin'
|
||||
|
||||
let assetIdAtHome = 'assetAtHome'
|
||||
const assetIdAtForeign = 'assetAtForeign'
|
||||
let assetAtHome: Asset
|
||||
let assetAtForeign: Asset
|
||||
|
||||
if (process.env.REACT_APP_MODE === 'AMB_NATIVE_TO_ERC677') {
|
||||
sPOA.setMediatorAddress(process.env.REACT_APP_HOME_MEDIATOR_ADDRESS)
|
||||
assetAtHome = sPOA
|
||||
assetIdAtHome = sPOA.id
|
||||
|
||||
assetAtForeign = new ERC677Asset({
|
||||
id: 'assetAtForeign',
|
||||
// @ts-ignore
|
||||
name: process.env.REACT_APP_FOREIGN_TOKEN_NAME,
|
||||
// @ts-ignore
|
||||
network: process.env.REACT_APP_FOREIGN_NETWORK,
|
||||
// @ts-ignore
|
||||
address: process.env.REACT_APP_FOREIGN_TOKEN_ADDRESS
|
||||
})
|
||||
} else {
|
||||
// process.env.REACT_APP_MODE === 'AMB_ERC677_TO_ERC677'
|
||||
assetAtHome = new ERC677Asset({
|
||||
id: 'assetAtHome',
|
||||
// @ts-ignore
|
||||
name: process.env.REACT_APP_HOME_TOKEN_NAME,
|
||||
// @ts-ignore
|
||||
network: process.env.REACT_APP_HOME_NETWORK,
|
||||
// @ts-ignore
|
||||
address: process.env.REACT_APP_HOME_TOKEN_ADDRESS
|
||||
})
|
||||
|
||||
assetAtForeign = new ERC677Asset({
|
||||
id: 'assetAtForeign',
|
||||
// @ts-ignore
|
||||
name: process.env.REACT_APP_FOREIGN_TOKEN_NAME,
|
||||
// @ts-ignore
|
||||
network: process.env.REACT_APP_FOREIGN_NETWORK,
|
||||
// @ts-ignore
|
||||
address: process.env.REACT_APP_FOREIGN_TOKEN_ADDRESS
|
||||
})
|
||||
}
|
||||
|
||||
const testBridge = new Mediator({
|
||||
assetA: assetIdAtHome,
|
||||
// @ts-ignore
|
||||
assetABridge: process.env.REACT_APP_HOME_MEDIATOR_ADDRESS,
|
||||
assetB: assetIdAtForeign,
|
||||
// @ts-ignore
|
||||
assetBBridge: process.env.REACT_APP_FOREIGN_MEDIATOR_ADDRESS
|
||||
})
|
||||
|
||||
const core = new BurnerCore({
|
||||
signers: [new InjectedSigner(), new LocalSigner({ privateKey: process.env.REACT_APP_PK, saveKey: false })],
|
||||
gateways: [new InjectedGateway(), new TokenBridgeGateway(), new InfuraGateway(process.env.REACT_APP_INFURA_KEY)],
|
||||
assets: [assetAtHome, assetAtForeign]
|
||||
})
|
||||
|
||||
const exchange = new Exchange([testBridge])
|
||||
|
||||
const BurnerWallet = () => <ModernUI title="Testing Wallet" core={core} plugins={[exchange, new MetamaskPlugin()]} />
|
||||
|
||||
ReactDOM.render(<BurnerWallet />, document.getElementById('root'))
|
1
burner-wallet-plugin/testing/src/react-app-env.d.ts
vendored
Normal file
1
burner-wallet-plugin/testing/src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
25
burner-wallet-plugin/testing/tsconfig.json
Normal file
25
burner-wallet-plugin/testing/tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
}
|
37
burner-wallet-plugin/tokenbridge-bw-exchange/README.md
Normal file
37
burner-wallet-plugin/tokenbridge-bw-exchange/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# TokenBridge Burner Wallet 2 Plugin
|
||||
|
||||
This plugin defines a Bridge trading pair to be used in the Exchange Plugin.
|
||||
|
||||
Bridge trading pairs and assets supported:
|
||||
* ETC - WETC Bridge
|
||||
|
||||
It also provides some generic resources that can be used and extended:
|
||||
* **ERC677Asset** - A representation of an Erc677 token
|
||||
* **NativeMediatorAsset** - Represents a native token that interacts with a Mediator extension.
|
||||
* **Mediator Pair** - Represents an Exchange Pair that interacts with mediators extensions.
|
||||
* **TokenBridgeGateway** - A gateway to operate with ETC, POA Sokol and POA Core networks.
|
||||
|
||||
### Install package
|
||||
```
|
||||
yarn add @poanet/tokenbridge-bw-exchange
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
```javascript
|
||||
import { Etc, Wetc, EtcGateway, WETCBridge } from '@poanet/tokenbridge-bw-exchange'
|
||||
|
||||
const core = new BurnerCore({
|
||||
...
|
||||
gateways: [new EtcGateway(), new InfuraGateway(process.env.REACT_APP_INFURA_KEY)],
|
||||
assets: [Etc, Wetc]
|
||||
})
|
||||
|
||||
const exchange = new Exchange({
|
||||
pairs: [new WETCBridge()]
|
||||
})
|
||||
```
|
||||
|
||||
This is how the exchange plugin will look like:
|
||||
|
||||
![exchange-wetc](https://user-images.githubusercontent.com/4614574/80991095-e40d0900-8e0d-11ea-9915-1b4e4a052694.png)
|
40
burner-wallet-plugin/tokenbridge-bw-exchange/package.json
Normal file
40
burner-wallet-plugin/tokenbridge-bw-exchange/package.json
Normal file
@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@poanet/tokenbridge-bw-exchange",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"/dist",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start-basic": "tsc -w",
|
||||
"start-local": "tsc -w",
|
||||
"test": "TS_NODE_PROJECT=\"tsconfig.testing.json\" mocha -r ts-node/register test/**/*.spec.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@burner-wallet/assets": "^1.1.10",
|
||||
"@burner-wallet/core": "^1.1.9",
|
||||
"@burner-wallet/exchange": "^1.1.4",
|
||||
"@burner-wallet/types": "^1.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/mocha": "^7.0.2",
|
||||
"chai": "^4.2.0",
|
||||
"mocha": "^5.2.0",
|
||||
"ts-node": "^8.8.2",
|
||||
"typescript": "^3.5.2"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/poanetwork/tokenbridge.git",
|
||||
"directory": "burner-wallet-plugin/tokenbridge-bw-exchange"
|
||||
},
|
||||
"homepage": "https://tokenbridge.net/",
|
||||
"keywords": [
|
||||
"tokenbridge",
|
||||
"burner-wallet"
|
||||
]
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
import { ERC20Asset } from '@burner-wallet/assets'
|
||||
import { ERC677_ABI } from '../../utils'
|
||||
|
||||
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
|
||||
const BLOCK_LOOKBACK = 250
|
||||
|
||||
interface ERC677Constructor {
|
||||
abi?: object
|
||||
address: string
|
||||
id: string
|
||||
name: string
|
||||
network: string
|
||||
}
|
||||
|
||||
export default class ERC677Asset extends ERC20Asset {
|
||||
constructor({ abi = ERC677_ABI, ...params }: ERC677Constructor) {
|
||||
super({ abi, type: 'erc677', ...params })
|
||||
}
|
||||
|
||||
async _send({ from, to, value }) {
|
||||
const receipt = await this.getContract()
|
||||
.methods.transferAndCall(to, value, '0x')
|
||||
.send({ from })
|
||||
return {
|
||||
...receipt,
|
||||
txHash: receipt.transactionHash,
|
||||
id: `${receipt.transactionHash}-${receipt.events.Transfer.logIndex}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides ERC20Asset `startWatchingAddress` to get the `Transfer` events by topic instead of
|
||||
* the event name because ERC677 abi has two events definitions named `Transfer` and
|
||||
* `getPastEvents` method does not provide a way to choose the correct one to use.
|
||||
* @param address
|
||||
*/
|
||||
startWatchingAddress(address) {
|
||||
let block = 0
|
||||
return this.poll(async () => {
|
||||
const currentBlock = await this.getWeb3().eth.getBlockNumber()
|
||||
if (block === 0) {
|
||||
block = Math.max(currentBlock - BLOCK_LOOKBACK, 0)
|
||||
}
|
||||
|
||||
const allTransferEvents = await this.getContract().getPastEvents('allEvents', {
|
||||
fromBlock: block,
|
||||
toBlock: currentBlock,
|
||||
topics: [TRANSFER_TOPIC]
|
||||
})
|
||||
// Manually filter `to` parameter because `filter` option does not work with allEvents
|
||||
const events = allTransferEvents.filter(e => e.returnValues.to.toLowerCase() === address.toLowerCase())
|
||||
|
||||
await events.map(async event =>
|
||||
this.core.addHistoryEvent({
|
||||
id: `${event.transactionHash}-${event.logIndex}`,
|
||||
asset: this.id,
|
||||
type: 'send',
|
||||
value: event.returnValues.value.toString(),
|
||||
from: event.returnValues.from,
|
||||
to: event.returnValues.to,
|
||||
tx: event.transactionHash,
|
||||
timestamp: await this._getBlockTimestamp(event.blockNumber)
|
||||
})
|
||||
)
|
||||
|
||||
block = currentBlock
|
||||
}, this._pollInterval)
|
||||
}
|
||||
|
||||
async getTx(txHash) {
|
||||
const historyEvents = this.core.getHistoryEvents({ asset: this.id })
|
||||
const eventMatch = historyEvents.filter(e => e.tx === txHash)
|
||||
if (eventMatch.length > 0) {
|
||||
return eventMatch[0]
|
||||
} else {
|
||||
return super.getTx(txHash)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
import NativeMediatorAsset from './NativeMediatorAsset'
|
||||
import { isBridgeContract, HOME_NATIVE_TO_ERC_ABI } from '../../utils'
|
||||
|
||||
class EtcNativeAsset extends NativeMediatorAsset {
|
||||
constructor(props) {
|
||||
super({ mediatorAddress: '0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9', ...props })
|
||||
}
|
||||
|
||||
async scanMediatorEvents(address, fromBlock, toBlock) {
|
||||
const web3 = this.getWeb3()
|
||||
const contract = new web3.eth.Contract(HOME_NATIVE_TO_ERC_ABI, this.mediatorAddress)
|
||||
const listenToBridgeEvent = await isBridgeContract(contract)
|
||||
if (listenToBridgeEvent && this.mediatorAddress != '') {
|
||||
const events = await contract.getPastEvents('AffirmationCompleted', {
|
||||
fromBlock,
|
||||
toBlock
|
||||
})
|
||||
const filteredEvents = events.filter(
|
||||
event => event.returnValues.recipient.toLowerCase() === address.toLowerCase()
|
||||
)
|
||||
|
||||
for (const event of filteredEvents) {
|
||||
this.core.addHistoryEvent({
|
||||
id: `${event.transactionHash}-${event.logIndex}`,
|
||||
asset: this.id,
|
||||
type: 'send',
|
||||
value: event.returnValues.value.toString(),
|
||||
from: this.mediatorAddress,
|
||||
to: event.returnValues.recipient,
|
||||
tx: event.transactionHash,
|
||||
timestamp: await this._getBlockTimestamp(event.blockNumber)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
await super.scanMediatorEvents(address, fromBlock, toBlock)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new EtcNativeAsset({
|
||||
id: 'etc',
|
||||
name: 'ETC',
|
||||
network: '61',
|
||||
icon: 'https://user-images.githubusercontent.com/4614574/77648741-666cf800-6f47-11ea-8cb4-01b9db00c264.png'
|
||||
})
|
65
burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/assets/NativeMediatorAsset.ts
Normal file
65
burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/assets/NativeMediatorAsset.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import { NativeAsset } from '@burner-wallet/assets'
|
||||
import { Contract, EventData } from 'web3-eth-contract'
|
||||
import { MEDIATOR_ABI } from '../../utils'
|
||||
|
||||
interface NativeMediatorConstructor {
|
||||
mediatorAddress?: string
|
||||
id: string
|
||||
name: string
|
||||
network: string
|
||||
}
|
||||
|
||||
export default class NativeMediatorAsset extends NativeAsset {
|
||||
protected mediatorAddress: string
|
||||
|
||||
constructor({ mediatorAddress = '', ...params }: NativeMediatorConstructor) {
|
||||
super({ ...params })
|
||||
this.mediatorAddress = mediatorAddress
|
||||
}
|
||||
|
||||
async scanBlocks(address, fromBlock, toBlock) {
|
||||
await super.scanBlocks(address, fromBlock, toBlock)
|
||||
await this.scanMediatorEvents(address, fromBlock, toBlock)
|
||||
}
|
||||
|
||||
async getTx(txHash) {
|
||||
const historyEvents = this.core.getHistoryEvents({ asset: this.id, account: this.mediatorAddress })
|
||||
const eventMatch = historyEvents.filter(e => e.tx === txHash)
|
||||
if (eventMatch.length > 0) {
|
||||
return eventMatch[0]
|
||||
} else {
|
||||
return super.getTx(txHash)
|
||||
}
|
||||
}
|
||||
|
||||
async scanMediatorEvents(address, fromBlock, toBlock) {
|
||||
if (this.mediatorAddress != '') {
|
||||
const web3 = this.getWeb3()
|
||||
const contract: Contract = new web3.eth.Contract(MEDIATOR_ABI, this.mediatorAddress)
|
||||
const events: EventData[] = await contract.getPastEvents('TokensBridged', {
|
||||
fromBlock,
|
||||
toBlock,
|
||||
filter: {
|
||||
recipient: address
|
||||
}
|
||||
})
|
||||
|
||||
for (const event of events) {
|
||||
this.core.addHistoryEvent({
|
||||
id: `${event.transactionHash}-${event.logIndex}`,
|
||||
asset: this.id,
|
||||
type: 'send',
|
||||
value: event.returnValues.value.toString(),
|
||||
from: this.mediatorAddress,
|
||||
to: event.returnValues.recipient,
|
||||
tx: event.transactionHash,
|
||||
timestamp: await this._getBlockTimestamp(event.blockNumber)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setMediatorAddress(mediatorAddress) {
|
||||
this.mediatorAddress = mediatorAddress
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { default as ERC677Asset } from './ERC677Asset'
|
||||
|
||||
export default new ERC677Asset({
|
||||
id: 'wetc',
|
||||
name: 'WETC',
|
||||
network: '1',
|
||||
address: '0x86aabcc646f290b9fc9bd05ce17c3858d1511da1'
|
||||
})
|
@ -0,0 +1,7 @@
|
||||
import NativeMediatorAsset from './NativeMediatorAsset'
|
||||
|
||||
export default new NativeMediatorAsset({
|
||||
id: 'spoa',
|
||||
name: 'sPOA',
|
||||
network: '77'
|
||||
})
|
54
burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/gateways/TokenBridgeGateway.ts
Normal file
54
burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/gateways/TokenBridgeGateway.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import { Gateway } from '@burner-wallet/core/gateways'
|
||||
import Web3 from 'web3'
|
||||
|
||||
export default class TokenBridgeGateway extends Gateway {
|
||||
private readonly providers: object
|
||||
private readonly providerStrings: { [id: string]: string }
|
||||
constructor() {
|
||||
super()
|
||||
this.providerStrings = {
|
||||
'61': `https://www.ethercluster.com/etc`,
|
||||
'77': 'https://sokol.poa.network',
|
||||
'99': 'https://core.poa.network'
|
||||
}
|
||||
this.providers = {}
|
||||
}
|
||||
|
||||
isAvailable() {
|
||||
return true
|
||||
}
|
||||
|
||||
getNetworks() {
|
||||
return ['61', '77', '99']
|
||||
}
|
||||
|
||||
_provider(network) {
|
||||
if (!this.providers[network]) {
|
||||
this._makeProvider(network)
|
||||
}
|
||||
return this.providers[network]
|
||||
}
|
||||
|
||||
_makeProvider(network) {
|
||||
if (!this.providerStrings[network]) {
|
||||
throw new Error(`Network ${network} not supported by TokenBridgeGateway`)
|
||||
}
|
||||
|
||||
this.providers[network] = new Web3.providers.HttpProvider(this.providerStrings[network])
|
||||
}
|
||||
|
||||
send(network, payload) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.getNetworks().indexOf(network) === -1) {
|
||||
return reject(new Error('TokenBridgeGateway does not support this network'))
|
||||
}
|
||||
|
||||
this._provider(network).send(payload, (err, response) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
return resolve(response.result)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
export { default as sPOA } from './assets/sPOA'
|
||||
export { default as Etc } from './assets/Etc'
|
||||
export { default as Wetc } from './assets/Wetc'
|
||||
export { default as ERC677Asset } from './assets/ERC677Asset'
|
||||
export { default as NativeMediatorAsset } from './assets/NativeMediatorAsset'
|
||||
export { default as TokenBridgeGateway } from './gateways/TokenBridgeGateway'
|
||||
export { default as Mediator } from './pairs/Mediator'
|
@ -0,0 +1,107 @@
|
||||
import BN from 'bn.js'
|
||||
import { Bridge, EstimateReturn, ValueTypes } from '@burner-wallet/exchange'
|
||||
import { waitForEvent, constants } from '../../utils'
|
||||
import { MEDIATOR_ABI, MEDIATOR_FEE_MANAGER_ABI } from '../../utils'
|
||||
import { fromWei, toBN } from 'web3-utils'
|
||||
|
||||
interface MediatorConstructor {
|
||||
assetA: string
|
||||
assetABridge: string
|
||||
assetB: string
|
||||
assetBBridge: string
|
||||
}
|
||||
|
||||
export default class Mediator extends Bridge {
|
||||
constructor({ assetA, assetABridge, assetB, assetBBridge }: MediatorConstructor) {
|
||||
super({ assetA, assetABridge, assetB, assetBBridge })
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async detectExchangeBToAFinished(account, value, sendResult) {
|
||||
const asset = this.getExchange().getAsset(this.assetA)
|
||||
const web3 = asset.getWeb3()
|
||||
const contract = new web3.eth.Contract(MEDIATOR_ABI, this.assetABridge)
|
||||
await waitForEvent(web3, contract, 'TokensBridged', this.processMediatorEvents(account))
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
async detectExchangeAToBFinished(account, value, sendResult) {
|
||||
const web3 = this.getExchange()
|
||||
.getAsset(this.assetB)
|
||||
.getWeb3()
|
||||
const contract = new web3.eth.Contract(MEDIATOR_ABI, this.assetBBridge)
|
||||
await waitForEvent(web3, contract, 'TokensBridged', this.processMediatorEvents(account))
|
||||
}
|
||||
|
||||
processMediatorEvents(account) {
|
||||
return events => {
|
||||
const confirmationEvent = events.filter(
|
||||
event => event.returnValues.recipient.toLowerCase() === account.toLowerCase()
|
||||
)
|
||||
return confirmationEvent.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
async estimateAtoB(value: ValueTypes): Promise<EstimateReturn> {
|
||||
const web3 = this.getExchange()
|
||||
.getAsset(this.assetB)
|
||||
.getWeb3()
|
||||
|
||||
const userAmount = this._getValue(value)
|
||||
|
||||
const contract = new web3.eth.Contract(MEDIATOR_ABI, this.assetBBridge)
|
||||
const { feeAmount, feePercentage } = await this.getFee(web3, contract, userAmount)
|
||||
const finalAmount = toBN(userAmount).sub(feeAmount)
|
||||
const estimateInfo = feeAmount.isZero() ? null : `${constants.ESTIMATE_FEE_MESSAGE} Fee: ${feePercentage}%`
|
||||
|
||||
return {
|
||||
estimate: finalAmount.toString(),
|
||||
estimateInfo
|
||||
}
|
||||
}
|
||||
|
||||
async estimateBtoA(value: ValueTypes): Promise<EstimateReturn> {
|
||||
const web3 = this.getExchange()
|
||||
.getAsset(this.assetA)
|
||||
.getWeb3()
|
||||
|
||||
const userAmount = this._getValue(value)
|
||||
|
||||
const contract = new web3.eth.Contract(MEDIATOR_ABI, this.assetABridge)
|
||||
const { feeAmount, feePercentage } = await this.getFee(web3, contract, userAmount)
|
||||
const finalAmount = toBN(userAmount).sub(feeAmount)
|
||||
const estimateInfo = feeAmount.isZero() ? null : `${constants.ESTIMATE_FEE_MESSAGE} Fee: ${feePercentage}%`
|
||||
|
||||
return {
|
||||
estimate: finalAmount.toString(),
|
||||
estimateInfo
|
||||
}
|
||||
}
|
||||
|
||||
async getFee(web3, contract, value): Promise<{ feeAmount: BN; feePercentage: number }> {
|
||||
const feeManagerAddress = await this.getFeeManagerContract(contract)
|
||||
if (feeManagerAddress != constants.ZERO_ADDRESS) {
|
||||
const feeManagerContract = new web3.eth.Contract(MEDIATOR_FEE_MANAGER_ABI, feeManagerAddress)
|
||||
const fee = toBN(await feeManagerContract.methods.fee().call())
|
||||
const feePercentage = Number(fromWei(fee, 'ether')) * 100
|
||||
const feeAmount = toBN(await feeManagerContract.methods.calculateFee(value).call())
|
||||
return {
|
||||
feeAmount,
|
||||
feePercentage
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
feeAmount: toBN(0),
|
||||
feePercentage: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getFeeManagerContract(contract) {
|
||||
try {
|
||||
return await contract.methods.feeManagerContract().call()
|
||||
} catch (e) {
|
||||
return constants.ZERO_ADDRESS
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export { ERC677Asset, NativeMediatorAsset, sPOA, Etc, Wetc, TokenBridgeGateway, Mediator } from './burner-wallet'
|
||||
export { WETCBridge } from './wetc-bridge'
|
@ -0,0 +1,5 @@
|
||||
export { default as ERC677_ABI } from './abis/ERC677'
|
||||
export { default as FOREIGN_NATIVE_TO_ERC_ABI } from './abis/ForeignBridgeNativeToErc'
|
||||
export { default as HOME_NATIVE_TO_ERC_ABI } from './abis/HomeBridgeNativeToErc'
|
||||
export { default as MEDIATOR_ABI } from './abis/Mediator'
|
||||
export { default as MEDIATOR_FEE_MANAGER_ABI } from './abis/MediatorFeeManager'
|
@ -0,0 +1,275 @@
|
||||
export default [
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_spender',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_value',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'approve',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'totalSupply',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_from',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_to',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_value',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'transferFrom',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_who',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'balanceOf',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_to',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_value',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'transfer',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_owner',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_spender',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'allowance',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'from',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: 'to',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'value',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'data',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'Transfer',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'owner',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: 'spender',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'value',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'Approval',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'from',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: 'to',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'value',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'Transfer',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'transferAndCall',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'spender',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: 'addedValue',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'increaseAllowance',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'spender',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: 'subtractedValue',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'decreaseAllowance',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
49
burner-wallet-plugin/tokenbridge-bw-exchange/src/utils/abis/ForeignBridgeNativeToErc.ts
Normal file
49
burner-wallet-plugin/tokenbridge-bw-exchange/src/utils/abis/ForeignBridgeNativeToErc.ts
Normal file
@ -0,0 +1,49 @@
|
||||
export default [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_txHash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'relayedMessages',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'deployedAtBlock',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'getHomeFee',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
@ -0,0 +1,52 @@
|
||||
export default [
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'recipient',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'value',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'transactionHash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'AffirmationCompleted',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'deployedAtBlock',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'getForeignFee',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
@ -0,0 +1,38 @@
|
||||
export default [
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'recipient',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'value',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: 'messageId',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'TokensBridged',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'feeManagerContract',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
@ -0,0 +1,35 @@
|
||||
export default [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'calculateFee',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'fee',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
@ -0,0 +1,8 @@
|
||||
export { constants, wait, waitForEvent, isBridgeContract } from './utils'
|
||||
export {
|
||||
ERC677_ABI,
|
||||
FOREIGN_NATIVE_TO_ERC_ABI,
|
||||
HOME_NATIVE_TO_ERC_ABI,
|
||||
MEDIATOR_ABI,
|
||||
MEDIATOR_FEE_MANAGER_ABI
|
||||
} from './abis'
|
@ -0,0 +1,40 @@
|
||||
import { EventData, Contract } from 'web3-eth-contract'
|
||||
import { toWei } from 'web3-utils'
|
||||
|
||||
export const wait = (time: number) => new Promise(resolve => setTimeout(resolve, time))
|
||||
|
||||
export const constants = {
|
||||
EXCHANGE_TIMEOUT: 300000,
|
||||
MAX_FEE: toWei('1', 'ether'),
|
||||
ESTIMATE_FEE_MESSAGE: 'Estimation takes fee charges into consideration.',
|
||||
ZERO_ADDRESS: '0x0000000000000000000000000000000000000000'
|
||||
}
|
||||
|
||||
export const waitForEvent = async (web3, contract: Contract, event: string, callback: Function) => {
|
||||
let fromBlock = await web3.eth.getBlockNumber()
|
||||
|
||||
const stopTime = Date.now() + constants.EXCHANGE_TIMEOUT
|
||||
while (Date.now() <= stopTime) {
|
||||
const currentBlock = await web3.eth.getBlockNumber()
|
||||
const events: EventData[] = await contract.getPastEvents(event, {
|
||||
fromBlock,
|
||||
toBlock: currentBlock
|
||||
})
|
||||
const eventFound = await callback(events)
|
||||
|
||||
if (eventFound) {
|
||||
return
|
||||
}
|
||||
fromBlock = currentBlock
|
||||
await wait(10000)
|
||||
}
|
||||
}
|
||||
|
||||
export const isBridgeContract = async (contract: Contract): Promise<boolean> => {
|
||||
try {
|
||||
await contract.methods.deployedAtBlock().call()
|
||||
return true
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
import { Mediator } from '../burner-wallet'
|
||||
import { HOME_NATIVE_TO_ERC_ABI, FOREIGN_NATIVE_TO_ERC_ABI } from '../utils'
|
||||
import { waitForEvent, isBridgeContract, constants } from '../utils'
|
||||
import { ValueTypes } from '@burner-wallet/exchange'
|
||||
import { toBN, fromWei } from 'web3-utils'
|
||||
|
||||
export default class WETCBridge extends Mediator {
|
||||
constructor() {
|
||||
super({
|
||||
assetA: 'etc',
|
||||
assetABridge: '0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9',
|
||||
assetB: 'wetc',
|
||||
assetBBridge: '0x0cB781EE62F815bdD9CD4c2210aE8600d43e7040'
|
||||
})
|
||||
}
|
||||
|
||||
async detectExchangeBToAFinished(account, value, sendResult) {
|
||||
const web3 = this.getExchange()
|
||||
.getAsset(this.assetA)
|
||||
.getWeb3()
|
||||
const contract = new web3.eth.Contract(HOME_NATIVE_TO_ERC_ABI, this.assetABridge)
|
||||
const listenToBridgeEvent = await isBridgeContract(contract)
|
||||
if (listenToBridgeEvent) {
|
||||
await waitForEvent(web3, contract, 'AffirmationCompleted', this.processBridgeEvents(sendResult.txHash))
|
||||
} else {
|
||||
await super.detectExchangeBToAFinished(account, value, sendResult)
|
||||
}
|
||||
}
|
||||
|
||||
async detectExchangeAToBFinished(account, value, sendResult) {
|
||||
const web3 = this.getExchange()
|
||||
.getAsset(this.assetB)
|
||||
.getWeb3()
|
||||
const contract = new web3.eth.Contract(FOREIGN_NATIVE_TO_ERC_ABI, this.assetBBridge)
|
||||
const listenToBridgeEvent = await isBridgeContract(contract)
|
||||
if (listenToBridgeEvent) {
|
||||
await waitForEvent(web3, contract, 'RelayedMessage', this.processBridgeEvents(sendResult.txHash))
|
||||
} else {
|
||||
await super.detectExchangeAToBFinished(account, value, sendResult)
|
||||
}
|
||||
}
|
||||
|
||||
processBridgeEvents(txHash) {
|
||||
return events => {
|
||||
const confirmationEvent = events.filter(event => event.returnValues.transactionHash === txHash)
|
||||
return confirmationEvent.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
async estimateAtoB(value: ValueTypes) {
|
||||
const web3 = this.getExchange()
|
||||
.getAsset(this.assetB)
|
||||
.getWeb3()
|
||||
const contract = new web3.eth.Contract(FOREIGN_NATIVE_TO_ERC_ABI, this.assetBBridge)
|
||||
|
||||
const useBridgeContract = await isBridgeContract(contract)
|
||||
|
||||
if (useBridgeContract) {
|
||||
const fee = toBN(await contract.methods.getHomeFee().call())
|
||||
const feeAmount = toBN(this._getValue(value))
|
||||
.mul(fee)
|
||||
.div(toBN(constants.MAX_FEE))
|
||||
const finalAmount = toBN(this._getValue(value)).sub(feeAmount)
|
||||
const feePercentage = Number(fromWei(fee, 'ether')) * 100
|
||||
const estimateInfo = feeAmount.isZero() ? null : `${constants.ESTIMATE_FEE_MESSAGE} Fee: ${feePercentage}%`
|
||||
|
||||
return {
|
||||
estimate: finalAmount.toString(),
|
||||
estimateInfo
|
||||
}
|
||||
} else {
|
||||
return await super.estimateAtoB(value)
|
||||
}
|
||||
}
|
||||
|
||||
async estimateBtoA(value: ValueTypes) {
|
||||
const web3 = this.getExchange()
|
||||
.getAsset(this.assetA)
|
||||
.getWeb3()
|
||||
const contract = new web3.eth.Contract(HOME_NATIVE_TO_ERC_ABI, this.assetABridge)
|
||||
|
||||
const useBridgeContract = await isBridgeContract(contract)
|
||||
|
||||
if (useBridgeContract) {
|
||||
const fee = toBN(await contract.methods.getForeignFee().call())
|
||||
const feeAmount = toBN(this._getValue(value))
|
||||
.mul(fee)
|
||||
.div(toBN(constants.MAX_FEE))
|
||||
const finalAmount = toBN(this._getValue(value)).sub(feeAmount)
|
||||
const feePercentage = Number(fromWei(fee, 'ether')) * 100
|
||||
const estimateInfo = feeAmount.isZero() ? null : `${constants.ESTIMATE_FEE_MESSAGE} Fee: ${feePercentage}%`
|
||||
return {
|
||||
estimate: finalAmount.toString(),
|
||||
estimateInfo
|
||||
}
|
||||
} else {
|
||||
return await super.estimateBtoA(value)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
export { default as WETCBridge } from './WETCBridge'
|
@ -0,0 +1,112 @@
|
||||
import { expect } from 'chai'
|
||||
import 'mocha'
|
||||
import ERC677Asset from '../src/burner-wallet/assets/ERC677Asset'
|
||||
|
||||
const ACCOUNT1 = '0x1010101010101010101010101010101010101010'
|
||||
const ACCOUNT2 = '0x0000000000000000000000000000000000000001'
|
||||
const TX_HASH = '0x376565f5614bd4483fd716c441aff43446b50f7772bef75496edef7faa070a85'
|
||||
const ONE_ETH = '1000000000000000000'
|
||||
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
|
||||
|
||||
describe('ERC677Asset', () => {
|
||||
it('should add an event when sent', done => {
|
||||
const asset = new ERC677Asset({
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
network: '5777',
|
||||
address: '0xcbfaa26289d24a6b4c5fe562bdd9a1b623260359'
|
||||
})
|
||||
|
||||
const testConditions = event => {
|
||||
expect(event.asset).to.equal('test')
|
||||
expect(event.type).to.equal('send')
|
||||
expect(event.value).to.equal(ONE_ETH)
|
||||
expect(event.from).to.equal(ACCOUNT2)
|
||||
expect(event.to).to.equal(ACCOUNT1)
|
||||
expect(event.id).to.equal(`${TX_HASH}-0`)
|
||||
expect(event.tx).to.equal(TX_HASH)
|
||||
done()
|
||||
}
|
||||
|
||||
const burnerCore = {
|
||||
addHistoryEvent: testConditions,
|
||||
getWeb3: () => ({
|
||||
eth: {
|
||||
methods: {},
|
||||
Contract: function Contract() {
|
||||
this.methods = {
|
||||
transferAndCall() {
|
||||
return {
|
||||
send() {
|
||||
return {
|
||||
transactionHash: TX_HASH,
|
||||
events: {
|
||||
Transfer: {
|
||||
logIndex: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
asset.setCore(burnerCore)
|
||||
asset.send({ to: ACCOUNT1, from: ACCOUNT2, value: ONE_ETH })
|
||||
})
|
||||
it('should watch an address and add events for new transactions', done => {
|
||||
const asset = new ERC677Asset({
|
||||
id: 'test',
|
||||
name: 'Test',
|
||||
network: '5777',
|
||||
address: '0xcbfaa26289d24a6b4c5fe562bdd9a1b623260359'
|
||||
})
|
||||
|
||||
const testConditions = event => {
|
||||
expect(event.asset).to.equal('test')
|
||||
expect(event.type).to.equal('send')
|
||||
expect(event.value).to.equal(ONE_ETH)
|
||||
expect(event.from).to.equal(ACCOUNT2)
|
||||
expect(event.to).to.equal(ACCOUNT1)
|
||||
expect(event.tx).to.equal(TX_HASH)
|
||||
expect(event.timestamp).to.equal(1571234034)
|
||||
done()
|
||||
}
|
||||
|
||||
const burnerCore = {
|
||||
addHistoryEvent: testConditions,
|
||||
getWeb3: () => ({
|
||||
eth: {
|
||||
getBlockNumber: () => 100,
|
||||
getBlock: () => ({ timestamp: 1571234034 }),
|
||||
Contract: function Contract() {
|
||||
// @ts-ignore
|
||||
this.getPastEvents = (eventName, { topics }) => {
|
||||
expect(eventName).to.equal('allEvents')
|
||||
expect(topics[0]).to.equal(TRANSFER_TOPIC)
|
||||
return [
|
||||
{
|
||||
event: 'Transfer',
|
||||
returnValues: {
|
||||
to: ACCOUNT1,
|
||||
from: ACCOUNT2,
|
||||
value: ONE_ETH
|
||||
},
|
||||
transactionHash: TX_HASH
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
asset.setCore(burnerCore)
|
||||
const unsubscribe = asset.startWatchingAddress(ACCOUNT1)
|
||||
unsubscribe()
|
||||
})
|
||||
})
|
10
burner-wallet-plugin/tokenbridge-bw-exchange/tsconfig.json
Normal file
10
burner-wallet-plugin/tokenbridge-bw-exchange/tsconfig.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "./dist"
|
||||
},
|
||||
"include": [
|
||||
"./src"
|
||||
]
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
}
|
||||
}
|
27
burner-wallet-plugin/tsconfig.json
Normal file
27
burner-wallet-plugin/tsconfig.json
Normal file
@ -0,0 +1,27 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"esModuleInterop": true,
|
||||
"jsx": "react",
|
||||
"target": "esnext",
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"noImplicitAny": false,
|
||||
"lib": [
|
||||
"es6",
|
||||
"dom"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
15931
burner-wallet-plugin/yarn.lock
Normal file
15931
burner-wallet-plugin/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
Subproject commit a5946e7024caf598e562da916675a3b269ab293d
|
||||
Subproject commit a7ce4441ab77e1c3e4d01017d862c53516933645
|
@ -21,6 +21,7 @@ In `group_vars/xdai.yml`
|
||||
---
|
||||
MONITOR_BRIDGE_NAME: "xdai"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_CACHE_EVENTS: "true"
|
||||
|
||||
COMMON_HOME_RPC_URL: "https://dai.poa.network"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
|
||||
@ -64,6 +65,7 @@ In `group_vars/wetc.yml`
|
||||
```
|
||||
---
|
||||
MONITOR_BRIDGE_NAME: "wetc"
|
||||
MONITOR_CACHE_EVENTS: "true"
|
||||
|
||||
COMMON_HOME_RPC_URL: "https://ethereumclassic.network"
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9"
|
||||
|
@ -44,6 +44,7 @@ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "xdai"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_CACHE_EVENTS: "true"
|
||||
MONITOR_HOME_START_BLOCK: 759
|
||||
MONITOR_FOREIGN_START_BLOCK: 6478417
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
|
@ -46,6 +46,7 @@ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_CACHE_EVENTS: "false"
|
||||
MONITOR_HOME_START_BLOCK: 0
|
||||
MONITOR_FOREIGN_START_BLOCK: 0
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
|
@ -43,6 +43,7 @@ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
#monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
MONITOR_CACHE_EVENTS: "true"
|
||||
MONITOR_HOME_START_BLOCK: 0
|
||||
MONITOR_FOREIGN_START_BLOCK: 0
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
|
@ -45,6 +45,7 @@ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "wetc"
|
||||
MONITOR_PORT: 3003
|
||||
MONITOR_CACHE_EVENTS: "true"
|
||||
MONITOR_HOME_START_BLOCK: 7703292
|
||||
MONITOR_FOREIGN_START_BLOCK: 7412459
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||
|
@ -32,3 +32,5 @@ MONITOR_FOREIGN_START_BLOCK={{ MONITOR_FOREIGN_START_BLOCK }}
|
||||
MONITOR_VALIDATOR_HOME_TX_LIMIT={{ MONITOR_VALIDATOR_HOME_TX_LIMIT }}
|
||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT={{ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT }}
|
||||
MONITOR_TX_NUMBER_THRESHOLD={{ MONITOR_TX_NUMBER_THRESHOLD }}
|
||||
|
||||
MONITOR_CACHE_EVENTS={{ MONITOR_CACHE_EVENTS }}
|
||||
|
@ -17,3 +17,4 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3013
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
MONITOR_CACHE_EVENTS=false
|
||||
|
@ -17,3 +17,4 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3012
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
MONITOR_CACHE_EVENTS=false
|
||||
|
@ -17,3 +17,4 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3011
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
MONITOR_CACHE_EVENTS=false
|
||||
|
@ -17,3 +17,4 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3010
|
||||
MONITOR_BRIDGE_NAME=bridge
|
||||
MONITOR_CACHE_EVENTS=false
|
||||
|
@ -32,7 +32,7 @@ FOREIGN_GAS_PRICE=10000000000
|
||||
FOREIGN_REWARDABLE=false
|
||||
|
||||
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
|
||||
ERC20_TOKEN_ADDRESS=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359
|
||||
ERC20_TOKEN_ADDRESS=0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9
|
||||
|
||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||
|
@ -7,8 +7,7 @@ const {
|
||||
addValidator,
|
||||
initializeChaiToken,
|
||||
convertDaiToChai,
|
||||
setMinDaiTokenBalance,
|
||||
migrateToMCD
|
||||
setMinDaiTokenBalance
|
||||
} = require('../utils')
|
||||
|
||||
const baseUrl = ercToNativeBridge.monitor
|
||||
@ -34,43 +33,12 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
||||
|
||||
it('should change balanceDiff', async function() {
|
||||
this.timeout(60000)
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.halfDuplexToken, ercToNativeBridge.foreign)
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.01 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === undefined &&
|
||||
investedErc20Balance === undefined
|
||||
)
|
||||
})
|
||||
|
||||
await migrateToMCD(foreignRPC.URL, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.01 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === '0' &&
|
||||
investedErc20Balance === undefined
|
||||
)
|
||||
})
|
||||
|
||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.halfDuplexToken, ercToNativeBridge.foreign)
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.02 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === undefined
|
||||
)
|
||||
const { erc20Balance, investedErc20Balance } = data.foreign
|
||||
return data.balanceDiff === 0.01 && erc20Balance === '0.01' && investedErc20Balance === undefined
|
||||
})
|
||||
})
|
||||
|
||||
@ -89,11 +57,10 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.03 &&
|
||||
data.balanceDiff === 0.02 &&
|
||||
erc20Balance === '0.02' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === '0' &&
|
||||
accumulatedInterest === '0.001' // value of dsrBalance() is initially defined in genesis block as 0.001
|
||||
)
|
||||
@ -104,11 +71,10 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.03 &&
|
||||
data.balanceDiff === 0.02 &&
|
||||
erc20Balance === '0.01' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === '0.01' &&
|
||||
accumulatedInterest === '0.001'
|
||||
)
|
||||
@ -119,11 +85,10 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
||||
|
||||
await waitUntil(async () => {
|
||||
;({ data } = await axios.get(`${baseUrl}`))
|
||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||
return (
|
||||
data.balanceDiff === 0.03 &&
|
||||
data.balanceDiff === 0.02 &&
|
||||
erc20Balance === '0.005' &&
|
||||
halfDuplexErc20Balance === '0.01' &&
|
||||
investedErc20Balance === '0.015' &&
|
||||
accumulatedInterest === '0.001'
|
||||
)
|
||||
|
@ -67,16 +67,6 @@ const addValidator = async (rpcUrl, account, bridgeAddress) => {
|
||||
})
|
||||
}
|
||||
|
||||
const migrateToMCD = async (rpcUrl, bridgeAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
const bridgeContract = new web3.eth.Contract(FOREIGN_ERC_TO_NATIVE_ABI, bridgeAddress)
|
||||
await bridgeContract.methods.migrateToMCD().send({
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
})
|
||||
}
|
||||
|
||||
const initializeChaiToken = async (rpcUrl, bridgeAddress) => {
|
||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||
@ -116,7 +106,6 @@ module.exports = {
|
||||
sendTokens,
|
||||
addValidator,
|
||||
sendAMBMessage,
|
||||
migrateToMCD,
|
||||
initializeChaiToken,
|
||||
setMinDaiTokenBalance,
|
||||
convertDaiToChai
|
||||
|
@ -21,3 +21,4 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
||||
|
||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||
MONITOR_PORT=3003
|
||||
MONITOR_CACHE_EVENTS=true
|
||||
|
@ -3,6 +3,7 @@ const BN = require('bignumber.js')
|
||||
const Web3 = require('web3')
|
||||
const logger = require('./logger')('getBalances')
|
||||
const { BRIDGE_MODES } = require('../commons')
|
||||
const { blockNumberHalfDuplexDisabled } = require('./utils/tokenUtils')
|
||||
|
||||
const Web3Utils = Web3.utils
|
||||
|
||||
@ -94,7 +95,9 @@ async function main(bridgeMode) {
|
||||
if (halfDuplexTokenAddress !== erc20Address) {
|
||||
const halfDuplexToken = new web3Foreign.eth.Contract(ERC20_ABI, halfDuplexTokenAddress)
|
||||
logger.debug('calling halfDuplexToken.methods.balanceOf')
|
||||
foreignHalfDuplexErc20Balance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
foreignHalfDuplexErc20Balance = await halfDuplexToken.methods
|
||||
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
.call(null, blockNumberHalfDuplexDisabled)
|
||||
logger.debug('getting last block numbers')
|
||||
const block = await web3Foreign.eth.getBlock('latest')
|
||||
|
||||
@ -139,8 +142,7 @@ async function main(bridgeMode) {
|
||||
const foreignErc20BalanceBN = new BN(foreignErc20Balance)
|
||||
const investedAmountInDaiBN = new BN(investedAmountInDai)
|
||||
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)
|
||||
const halfDuplexErc20BalanceBN =
|
||||
displayHalfDuplexToken && tokenSwapAllowed ? new BN(foreignHalfDuplexErc20Balance) : new BN(0)
|
||||
const halfDuplexErc20BalanceBN = displayHalfDuplexToken ? new BN(foreignHalfDuplexErc20Balance) : new BN(0)
|
||||
|
||||
const diff = foreignErc20BalanceBN
|
||||
.plus(halfDuplexErc20BalanceBN)
|
||||
|
@ -16,12 +16,14 @@ const {
|
||||
} = require('../../commons')
|
||||
const { normalizeEventInformation } = require('./message')
|
||||
const { filterTransferBeforeES } = require('./tokenUtils')
|
||||
const { writeFile, readCacheFile } = require('./file')
|
||||
|
||||
const {
|
||||
COMMON_HOME_RPC_URL,
|
||||
COMMON_FOREIGN_RPC_URL,
|
||||
COMMON_HOME_BRIDGE_ADDRESS,
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||
MONITOR_CACHE_EVENTS
|
||||
} = process.env
|
||||
const MONITOR_HOME_START_BLOCK = toBN(Number(process.env.MONITOR_HOME_START_BLOCK) || 0)
|
||||
const MONITOR_FOREIGN_START_BLOCK = toBN(Number(process.env.MONITOR_FOREIGN_START_BLOCK) || 0)
|
||||
@ -34,7 +36,17 @@ const web3Foreign = new Web3(foreignProvider)
|
||||
|
||||
const { getBlockNumber } = require('./contract')
|
||||
|
||||
const cacheFilePath = '/tmp/cachedEvents.json'
|
||||
async function main(mode) {
|
||||
if (MONITOR_CACHE_EVENTS === 'true') {
|
||||
logger.debug('checking existing events cache')
|
||||
const cachedEvents = readCacheFile(cacheFilePath)
|
||||
if (cachedEvents !== false) {
|
||||
logger.debug('returning events stored in cache')
|
||||
return cachedEvents
|
||||
}
|
||||
}
|
||||
|
||||
const homeErcBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||
const bridgeMode = mode || (await getBridgeMode(homeErcBridge))
|
||||
const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(bridgeMode)
|
||||
@ -146,11 +158,7 @@ async function main(mode) {
|
||||
})).map(normalizeEvent)
|
||||
|
||||
// Remove events after the ES
|
||||
const validHalfDuplexTransfers = await filterTransferBeforeES(
|
||||
halfDuplexTransferEvents,
|
||||
web3Foreign,
|
||||
foreignBridge
|
||||
)
|
||||
const validHalfDuplexTransfers = await filterTransferBeforeES(halfDuplexTransferEvents)
|
||||
|
||||
transferEvents = [...validHalfDuplexTransfers, ...transferEvents]
|
||||
})
|
||||
@ -174,7 +182,7 @@ async function main(mode) {
|
||||
}
|
||||
|
||||
logger.debug('Done')
|
||||
return {
|
||||
const result = {
|
||||
homeToForeignRequests,
|
||||
homeToForeignConfirmations,
|
||||
foreignToHomeConfirmations,
|
||||
@ -182,6 +190,12 @@ async function main(mode) {
|
||||
isExternalErc20,
|
||||
bridgeMode
|
||||
}
|
||||
|
||||
if (MONITOR_CACHE_EVENTS === 'true') {
|
||||
logger.debug('saving obtained events into cache file')
|
||||
writeFile(cacheFilePath, result, false)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports = main
|
||||
|
@ -15,8 +15,9 @@ async function readFile(filePath) {
|
||||
}
|
||||
}
|
||||
|
||||
function writeFile(filePath, object) {
|
||||
fs.writeFileSync(path.join(process.cwd(), filePath), JSON.stringify(object, null, 4))
|
||||
function writeFile(filePath, object, useCwd = true) {
|
||||
const fullPath = useCwd ? path.join(process.cwd(), filePath) : filePath
|
||||
fs.writeFileSync(fullPath, JSON.stringify(object, null, 4))
|
||||
}
|
||||
|
||||
function createDir(dirPath) {
|
||||
@ -29,8 +30,17 @@ function createDir(dirPath) {
|
||||
}
|
||||
}
|
||||
|
||||
function readCacheFile(filePath) {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(filePath))
|
||||
} catch (_) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
readFile,
|
||||
writeFile,
|
||||
createDir
|
||||
createDir,
|
||||
readCacheFile
|
||||
}
|
||||
|
@ -1,32 +1,18 @@
|
||||
let beforeESBiggestBlockNumber = 0
|
||||
// https://etherscan.io/tx/0xd0c3c92c94e05bc71256055ce8c4c993e047f04e04f3283a04e4cb077b71f6c6
|
||||
const blockNumberHalfDuplexDisabled = 9884448
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns true if the event was before the Emergency Shutdown.
|
||||
* The method has an optimization to avoid making request if a bigger block number is confirmed
|
||||
* to be before the ES. Events should be iterated from newer to older order to use the optimization.
|
||||
* Returns true if the event was before the bridge stopped supporting half duplex transfers.
|
||||
*/
|
||||
async function transferBeforeES(event, web3Foreign, foreignBridge) {
|
||||
const { blockNumber } = event
|
||||
|
||||
if (blockNumber < beforeESBiggestBlockNumber) {
|
||||
return true
|
||||
}
|
||||
|
||||
const block = await web3Foreign.eth.getBlock(blockNumber)
|
||||
|
||||
const tokenSwapAllowed = await foreignBridge.methods.isTokenSwapAllowed(block.timestamp).call()
|
||||
if (tokenSwapAllowed) {
|
||||
beforeESBiggestBlockNumber = blockNumber
|
||||
}
|
||||
return tokenSwapAllowed
|
||||
async function transferBeforeES(event) {
|
||||
return event.blockNumber < blockNumberHalfDuplexDisabled
|
||||
}
|
||||
|
||||
async function filterTransferBeforeES(array, web3Foreign, foreignBridge) {
|
||||
async function filterTransferBeforeES(array) {
|
||||
const newArray = []
|
||||
// Iterate events from newer to older
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
const beforeES = await transferBeforeES(array[i], web3Foreign, foreignBridge)
|
||||
const beforeES = await transferBeforeES(array[i])
|
||||
if (beforeES) {
|
||||
// add element to first position so the new array will have the same order
|
||||
newArray.unshift(array[i])
|
||||
@ -36,5 +22,6 @@ async function filterTransferBeforeES(array, web3Foreign, foreignBridge) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
filterTransferBeforeES
|
||||
filterTransferBeforeES,
|
||||
blockNumberHalfDuplexDisabled
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ const {
|
||||
homeRPC,
|
||||
foreignRPC
|
||||
} = require('../../e2e-commons/constants.json')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, SAI_TOP, HOME_ERC_TO_NATIVE_ABI } = require('../../commons')
|
||||
const { uniformRetry, sleep } = require('../../e2e-commons/utils')
|
||||
const { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, HOME_ERC_TO_NATIVE_ABI } = require('../../commons')
|
||||
const { uniformRetry } = require('../../e2e-commons/utils')
|
||||
const { setRequiredSignatures } = require('./utils')
|
||||
|
||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
||||
@ -59,18 +59,35 @@ describe('erc to native', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should continue working after migration', async () => {
|
||||
it('should not convert half duplex tokens to native tokens in home', async () => {
|
||||
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
|
||||
const transferValue = homeWeb3.utils.toWei('0.01')
|
||||
|
||||
// erc20 token address and half duplex address are the same before migration
|
||||
const tokenAddress = await foreignBridge.methods.erc20token().call()
|
||||
// send tokens to foreign bridge
|
||||
await halfDuplexToken.methods
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
const erc20AndhalfDuplexToken = new foreignWeb3.eth.Contract(ERC677_BRIDGE_TOKEN_ABI, tokenAddress)
|
||||
// check that balance does not increases
|
||||
await promiseRetry(async (retry, number) => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
// retry at least 4 times to check transfer is not processed
|
||||
if (toBN(balance).eq(toBN(originalBalanceOnHome)) && number < 4) {
|
||||
retry()
|
||||
} else {
|
||||
assert(toBN(balance).eq(toBN(originalBalanceOnHome)), 'User balance should not be increased')
|
||||
}
|
||||
})
|
||||
|
||||
// send tokens to foreign bridge
|
||||
await erc20AndhalfDuplexToken.methods
|
||||
await erc20Token.methods
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
||||
.send({
|
||||
from: user.address,
|
||||
@ -94,19 +111,7 @@ describe('erc to native', () => {
|
||||
}
|
||||
})
|
||||
|
||||
// call migration
|
||||
await foreignBridge.methods.migrateToMCD().send({
|
||||
from: validator.address,
|
||||
gas: '4000000'
|
||||
})
|
||||
|
||||
// update min threshold for swap
|
||||
await foreignBridge.methods.setMinHDTokenBalance(foreignWeb3.utils.toWei('2', 'ether')).send({
|
||||
from: validator.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
const AfterMigrateBalance = await homeWeb3.eth.getBalance(user.address)
|
||||
const afterTransferBalance = await homeWeb3.eth.getBalance(user.address)
|
||||
|
||||
// send tokens to foreign bridge
|
||||
await erc20Token.methods
|
||||
@ -123,39 +128,12 @@ describe('erc to native', () => {
|
||||
await promiseRetry(async (retry, number) => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
// retry at least 4 times to check transfer is not double processed by the two watchers
|
||||
if (toBN(balance).lte(toBN(AfterMigrateBalance)) || number < 4) {
|
||||
if (toBN(balance).lte(toBN(afterTransferBalance)) || number < 4) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(balance).eq(toBN(AfterMigrateBalance).add(toBN(transferValue))),
|
||||
'User balance should be increased only by second transfer'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const afterMigrateAndTransferBalance = await homeWeb3.eth.getBalance(user.address)
|
||||
|
||||
// send tokens to foreign bridge
|
||||
await halfDuplexToken.methods
|
||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await promiseRetry(async (retry, number) => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
// retry at least 4 times to check transfer is not double processed by the two watchers
|
||||
if (toBN(balance).lte(toBN(afterMigrateAndTransferBalance)) || number < 4) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(balance).eq(toBN(afterMigrateAndTransferBalance).add(toBN(transferValue))),
|
||||
'User balance should be increased only by second transfer'
|
||||
toBN(balance).eq(toBN(afterTransferBalance).add(toBN(transferValue))),
|
||||
'User balance should be increased'
|
||||
)
|
||||
}
|
||||
})
|
||||
@ -224,223 +202,6 @@ describe('erc to native', () => {
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should convert half duplex token in foreign to native token in home', async () => {
|
||||
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
const bridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether')
|
||||
|
||||
// this transfer won't trigger a call to swap tokens
|
||||
await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await uniformRetry(async retry => {
|
||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||
if (toBN(balance).lte(toBN(originalBalanceOnHome))) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(balance).eq(toBN(originalBalanceOnHome).add(toBN(valueToTransfer))),
|
||||
'User balance should be increased by the half duplex token transfer'
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
const updatedBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const updatedBridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(
|
||||
toBN(updatedBridgeHalfDuplexBalance).eq(toBN(bridgeHalfDuplexBalance).add(toBN(valueToTransfer))),
|
||||
'Bridge balance should reflect the transfer value'
|
||||
)
|
||||
|
||||
// this transfer will trigger call to swap tokens
|
||||
await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
await sleep(2000)
|
||||
|
||||
await uniformRetry(async retry => {
|
||||
const userBalance = await homeWeb3.eth.getBalance(user.address)
|
||||
const updatedBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
if (
|
||||
toBN(userBalance).lte(toBN(updatedBalanceOnHome)) ||
|
||||
toBN(updatedBridgeErc20TokenBalance).lte(toBN(bridgeErc20TokenBalance))
|
||||
) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(userBalance).eq(toBN(updatedBalanceOnHome).add(toBN(valueToTransfer))),
|
||||
'User balance should be increased by the half duplex token transfer'
|
||||
)
|
||||
const updatedBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(toBN(updatedBalance).isZero(), 'Half duplex bridge balance should be zero')
|
||||
assert(
|
||||
toBN(updatedBridgeErc20TokenBalance).eq(
|
||||
toBN(bridgeErc20TokenBalance)
|
||||
.add(toBN(bridgeHalfDuplexBalance))
|
||||
.add(toBN(foreignWeb3.utils.toWei('2', 'ether')))
|
||||
),
|
||||
'Erc20 token balance should be correctly increased by the token swap'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should convert half duplex token in foreign to native token in home for alternative receiver ', async () => {
|
||||
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const initialBalanceSecondUser = await homeWeb3.eth.getBalance(secondUser.address)
|
||||
const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether')
|
||||
|
||||
// approve tokens to foreign bridge
|
||||
await halfDuplexToken.methods
|
||||
.approve(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// call bridge method to transfer tokens to a different recipient
|
||||
await foreignBridge.methods['relayTokens(address,uint256,address)'](
|
||||
secondUser.address,
|
||||
valueToTransfer,
|
||||
halfDuplexTokenAddress
|
||||
)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
// check that balance increases
|
||||
await uniformRetry(async retry => {
|
||||
const secondUserbalance = await homeWeb3.eth.getBalance(secondUser.address)
|
||||
const updatedBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
const userbalance = await homeWeb3.eth.getBalance(user.address)
|
||||
assert(toBN(userbalance).lte(toBN(originalBalanceOnHome)), 'User balance should be the same')
|
||||
if (
|
||||
toBN(secondUserbalance).lte(toBN(initialBalanceSecondUser)) ||
|
||||
toBN(updatedBridgeErc20TokenBalance).lte(toBN(bridgeErc20TokenBalance))
|
||||
) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(secondUserbalance).eq(toBN(initialBalanceSecondUser).add(toBN(valueToTransfer))),
|
||||
'User balance should be increased by the half duplex token transfer'
|
||||
)
|
||||
const updatedHDBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(toBN(updatedHDBalance).isZero(), 'Half duplex bridge balance should be zero')
|
||||
assert(
|
||||
toBN(updatedBridgeErc20TokenBalance).eq(toBN(bridgeErc20TokenBalance).add(toBN(valueToTransfer))),
|
||||
'Erc20 token balance should be correctly increased by the token swap'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should not relay half duplex token transfer after Emergency Shutdown', async () => {
|
||||
const originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const bridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
const bridgeHalfDuplexBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
const block = await foreignWeb3.eth.getBlock('latest')
|
||||
const saiTop = new foreignWeb3.eth.Contract(SAI_TOP, ercToNativeBridge.saiTop)
|
||||
|
||||
// Trigger Emergency Shutdown
|
||||
await saiTop.methods
|
||||
.setCaged(block.timestamp)
|
||||
.send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
.catch(e => {
|
||||
console.error(e)
|
||||
})
|
||||
|
||||
const valueToTransfer = foreignWeb3.utils.toWei('1', 'ether')
|
||||
|
||||
await sleep(2000)
|
||||
|
||||
// this transfer won't trigger a call to swap tokens
|
||||
await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, valueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
// check that transfer and swap are not processed in the next blocks.
|
||||
await promiseRetry(async (retry, number) => {
|
||||
const balanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||
const currentBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
const currentBridgeHalfDuplexBalance = await halfDuplexToken.methods
|
||||
.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS)
|
||||
.call()
|
||||
|
||||
assert(toBN(balanceOnHome).eq(toBN(originalBalanceOnHome)), 'User balance should be the same')
|
||||
assert(
|
||||
toBN(currentBridgeHalfDuplexBalance).eq(toBN(bridgeHalfDuplexBalance).add(toBN(valueToTransfer))),
|
||||
'Half duplex balance should be the value of transfer'
|
||||
)
|
||||
assert(toBN(currentBridgeErc20TokenBalance).eq(toBN(bridgeErc20TokenBalance)), 'erc20 balance should not change')
|
||||
|
||||
// after several retries, the state is corrects
|
||||
if (number < 4) {
|
||||
retry()
|
||||
}
|
||||
})
|
||||
|
||||
// let's undo the Emergency Shutdown to check that the oracle is still working
|
||||
await saiTop.methods.setCaged('0').send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
const newValueToTransfer = foreignWeb3.utils.toWei('2', 'ether')
|
||||
|
||||
await halfDuplexToken.methods.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, newValueToTransfer).send({
|
||||
from: user.address,
|
||||
gas: '1000000'
|
||||
})
|
||||
|
||||
await sleep(2000)
|
||||
|
||||
await uniformRetry(async retry => {
|
||||
const userBalance = await homeWeb3.eth.getBalance(user.address)
|
||||
const updatedBridgeErc20TokenBalance = await erc20Token.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
|
||||
if (
|
||||
toBN(userBalance).lte(toBN(originalBalanceOnHome)) ||
|
||||
toBN(updatedBridgeErc20TokenBalance).lte(toBN(bridgeErc20TokenBalance))
|
||||
) {
|
||||
retry()
|
||||
} else {
|
||||
assert(
|
||||
toBN(userBalance).eq(toBN(originalBalanceOnHome).add(toBN(newValueToTransfer))),
|
||||
'User balance should be increased by the half duplex token transfer'
|
||||
)
|
||||
const updatedHDBalance = await halfDuplexToken.methods.balanceOf(COMMON_FOREIGN_BRIDGE_ADDRESS).call()
|
||||
assert(toBN(updatedHDBalance).isZero(), 'Half duplex bridge balance should be zero')
|
||||
assert(
|
||||
toBN(updatedBridgeErc20TokenBalance).eq(
|
||||
toBN(bridgeErc20TokenBalance)
|
||||
.add(toBN(valueToTransfer))
|
||||
.add(toBN(newValueToTransfer))
|
||||
),
|
||||
'Erc20 token balance should be correctly increased by the token swap'
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
it('should convert coins in home to tokens in foreign', async () => {
|
||||
const originalBalance = await erc20Token.methods.balanceOf(user.address).call()
|
||||
|
||||
|
@ -12,10 +12,8 @@
|
||||
"eslint": "5.16.0",
|
||||
"eslint-config-airbnb": "17.1.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.1",
|
||||
"eslint-plugin-import": "2.14.0",
|
||||
"eslint-plugin-node": "7.0.1",
|
||||
"eslint-plugin-prettier": "2.6.2",
|
||||
"eslint-config-react-app": "^4.0.1",
|
||||
"eslint-plugin-react": "^7.13.0",
|
||||
"eslint-plugin-react-hooks": "^1.6.0",
|
||||
"eslint-plugin-flowtype": "^3.8.1",
|
||||
@ -30,16 +28,18 @@
|
||||
"ui-e2e",
|
||||
"monitor",
|
||||
"monitor-e2e",
|
||||
"contracts"
|
||||
"contracts",
|
||||
"burner-wallet-plugin"
|
||||
],
|
||||
"scripts": {
|
||||
"initialize": "yarn clean && git submodule update --init && yarn install --unsafe-perm --frozen-lockfile && yarn install:deploy && yarn compile:contracts",
|
||||
"build": "yarn workspace ui run build",
|
||||
"build:plugin": "yarn workspace burner-wallet-plugin run build",
|
||||
"lint": "yarn wsrun --exclude token-bridge-contracts lint",
|
||||
"test": "yarn wsrun --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e test",
|
||||
"oracle-e2e": "./oracle-e2e/run-tests.sh",
|
||||
"ui-e2e": "./ui-e2e/run-tests.sh",
|
||||
"clean": "rm -rf ./node_modules ./**/node_modules ./**/**/node_modules ./**/build",
|
||||
"clean": "rm -rf ./node_modules ./**/node_modules ./**/**/node_modules ./**/build ./**/**/dist",
|
||||
"compile:contracts": "yarn workspace token-bridge-contracts run compile",
|
||||
"install:deploy": "cd contracts/deploy && npm install --unsafe-perm --silent",
|
||||
"postinstall": "ln -sf $(pwd)/node_modules/openzeppelin-solidity/ contracts/node_modules/openzeppelin-solidity"
|
||||
|
Loading…
Reference in New Issue
Block a user