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
|
submodules
|
||||||
coverage
|
coverage
|
||||||
lib
|
lib
|
||||||
|
dist
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,6 +6,7 @@ coverage
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
build
|
build
|
||||||
|
dist
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
@ -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_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_PORT | The port for the Monitor. | integer
|
||||||
MONITOR_BRIDGE_NAME | The name to be used in the url path for the bridge | string
|
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_BRIDGE_NAME: "xdai"
|
||||||
MONITOR_PORT: 3003
|
MONITOR_PORT: 3003
|
||||||
|
MONITOR_CACHE_EVENTS: "true"
|
||||||
|
|
||||||
COMMON_HOME_RPC_URL: "https://dai.poa.network"
|
COMMON_HOME_RPC_URL: "https://dai.poa.network"
|
||||||
COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
|
COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6"
|
||||||
@ -64,6 +65,7 @@ In `group_vars/wetc.yml`
|
|||||||
```
|
```
|
||||||
---
|
---
|
||||||
MONITOR_BRIDGE_NAME: "wetc"
|
MONITOR_BRIDGE_NAME: "wetc"
|
||||||
|
MONITOR_CACHE_EVENTS: "true"
|
||||||
|
|
||||||
COMMON_HOME_RPC_URL: "https://ethereumclassic.network"
|
COMMON_HOME_RPC_URL: "https://ethereumclassic.network"
|
||||||
COMMON_HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9"
|
COMMON_HOME_BRIDGE_ADDRESS: "0x073081832B4Ecdce79d4D6753565c85Ba4b3BeA9"
|
||||||
|
@ -44,6 +44,7 @@ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
|||||||
## Monitor
|
## Monitor
|
||||||
MONITOR_BRIDGE_NAME: "xdai"
|
MONITOR_BRIDGE_NAME: "xdai"
|
||||||
MONITOR_PORT: 3003
|
MONITOR_PORT: 3003
|
||||||
|
MONITOR_CACHE_EVENTS: "true"
|
||||||
MONITOR_HOME_START_BLOCK: 759
|
MONITOR_HOME_START_BLOCK: 759
|
||||||
MONITOR_FOREIGN_START_BLOCK: 6478417
|
MONITOR_FOREIGN_START_BLOCK: 6478417
|
||||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||||
|
@ -46,6 +46,7 @@ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
|||||||
## Monitor
|
## Monitor
|
||||||
MONITOR_BRIDGE_NAME: "bridge"
|
MONITOR_BRIDGE_NAME: "bridge"
|
||||||
MONITOR_PORT: 3003
|
MONITOR_PORT: 3003
|
||||||
|
MONITOR_CACHE_EVENTS: "false"
|
||||||
MONITOR_HOME_START_BLOCK: 0
|
MONITOR_HOME_START_BLOCK: 0
|
||||||
MONITOR_FOREIGN_START_BLOCK: 0
|
MONITOR_FOREIGN_START_BLOCK: 0
|
||||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||||
|
@ -43,6 +43,7 @@ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
|||||||
|
|
||||||
#monitor
|
#monitor
|
||||||
MONITOR_BRIDGE_NAME: "bridge"
|
MONITOR_BRIDGE_NAME: "bridge"
|
||||||
|
MONITOR_CACHE_EVENTS: "true"
|
||||||
MONITOR_HOME_START_BLOCK: 0
|
MONITOR_HOME_START_BLOCK: 0
|
||||||
MONITOR_FOREIGN_START_BLOCK: 0
|
MONITOR_FOREIGN_START_BLOCK: 0
|
||||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
||||||
|
@ -45,6 +45,7 @@ UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
|||||||
## Monitor
|
## Monitor
|
||||||
MONITOR_BRIDGE_NAME: "wetc"
|
MONITOR_BRIDGE_NAME: "wetc"
|
||||||
MONITOR_PORT: 3003
|
MONITOR_PORT: 3003
|
||||||
|
MONITOR_CACHE_EVENTS: "true"
|
||||||
MONITOR_HOME_START_BLOCK: 7703292
|
MONITOR_HOME_START_BLOCK: 7703292
|
||||||
MONITOR_FOREIGN_START_BLOCK: 7412459
|
MONITOR_FOREIGN_START_BLOCK: 7412459
|
||||||
MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000
|
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_HOME_TX_LIMIT={{ MONITOR_VALIDATOR_HOME_TX_LIMIT }}
|
||||||
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT={{ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT }}
|
MONITOR_VALIDATOR_FOREIGN_TX_LIMIT={{ MONITOR_VALIDATOR_FOREIGN_TX_LIMIT }}
|
||||||
MONITOR_TX_NUMBER_THRESHOLD={{ MONITOR_TX_NUMBER_THRESHOLD }}
|
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_TX_NUMBER_THRESHOLD=100
|
||||||
MONITOR_PORT=3013
|
MONITOR_PORT=3013
|
||||||
MONITOR_BRIDGE_NAME=bridge
|
MONITOR_BRIDGE_NAME=bridge
|
||||||
|
MONITOR_CACHE_EVENTS=false
|
||||||
|
@ -17,3 +17,4 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
|||||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||||
MONITOR_PORT=3012
|
MONITOR_PORT=3012
|
||||||
MONITOR_BRIDGE_NAME=bridge
|
MONITOR_BRIDGE_NAME=bridge
|
||||||
|
MONITOR_CACHE_EVENTS=false
|
||||||
|
@ -17,3 +17,4 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
|||||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||||
MONITOR_PORT=3011
|
MONITOR_PORT=3011
|
||||||
MONITOR_BRIDGE_NAME=bridge
|
MONITOR_BRIDGE_NAME=bridge
|
||||||
|
MONITOR_CACHE_EVENTS=false
|
||||||
|
@ -17,3 +17,4 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
|||||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||||
MONITOR_PORT=3010
|
MONITOR_PORT=3010
|
||||||
MONITOR_BRIDGE_NAME=bridge
|
MONITOR_BRIDGE_NAME=bridge
|
||||||
|
MONITOR_CACHE_EVENTS=false
|
||||||
|
@ -32,7 +32,7 @@ FOREIGN_GAS_PRICE=10000000000
|
|||||||
FOREIGN_REWARDABLE=false
|
FOREIGN_REWARDABLE=false
|
||||||
|
|
||||||
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
|
BLOCK_REWARD_ADDRESS=0xF9698Eb93702dfdd0e2d802088d4c21822a8A977
|
||||||
ERC20_TOKEN_ADDRESS=0x89d24A6b4CcB1B6fAA2625fE562bDD9a23260359
|
ERC20_TOKEN_ADDRESS=0x7cc4b1851c35959d34e635a470f6b5c43ba3c9c9
|
||||||
|
|
||||||
REQUIRED_NUMBER_OF_VALIDATORS=1
|
REQUIRED_NUMBER_OF_VALIDATORS=1
|
||||||
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d"
|
||||||
|
@ -7,8 +7,7 @@ const {
|
|||||||
addValidator,
|
addValidator,
|
||||||
initializeChaiToken,
|
initializeChaiToken,
|
||||||
convertDaiToChai,
|
convertDaiToChai,
|
||||||
setMinDaiTokenBalance,
|
setMinDaiTokenBalance
|
||||||
migrateToMCD
|
|
||||||
} = require('../utils')
|
} = require('../utils')
|
||||||
|
|
||||||
const baseUrl = ercToNativeBridge.monitor
|
const baseUrl = ercToNativeBridge.monitor
|
||||||
@ -34,43 +33,12 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
|||||||
|
|
||||||
it('should change balanceDiff', async function() {
|
it('should change balanceDiff', async function() {
|
||||||
this.timeout(60000)
|
this.timeout(60000)
|
||||||
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.halfDuplexToken, ercToNativeBridge.foreign)
|
await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign)
|
||||||
|
|
||||||
await waitUntil(async () => {
|
await waitUntil(async () => {
|
||||||
;({ data } = await axios.get(`${baseUrl}`))
|
;({ data } = await axios.get(`${baseUrl}`))
|
||||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance } = data.foreign
|
const { erc20Balance, investedErc20Balance } = data.foreign
|
||||||
return (
|
return data.balanceDiff === 0.01 && erc20Balance === '0.01' && investedErc20Balance === undefined
|
||||||
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
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -89,11 +57,10 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
|||||||
|
|
||||||
await waitUntil(async () => {
|
await waitUntil(async () => {
|
||||||
;({ data } = await axios.get(`${baseUrl}`))
|
;({ data } = await axios.get(`${baseUrl}`))
|
||||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||||
return (
|
return (
|
||||||
data.balanceDiff === 0.03 &&
|
data.balanceDiff === 0.02 &&
|
||||||
erc20Balance === '0.02' &&
|
erc20Balance === '0.02' &&
|
||||||
halfDuplexErc20Balance === '0.01' &&
|
|
||||||
investedErc20Balance === '0' &&
|
investedErc20Balance === '0' &&
|
||||||
accumulatedInterest === '0.001' // value of dsrBalance() is initially defined in genesis block as 0.001
|
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 () => {
|
await waitUntil(async () => {
|
||||||
;({ data } = await axios.get(`${baseUrl}`))
|
;({ data } = await axios.get(`${baseUrl}`))
|
||||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||||
return (
|
return (
|
||||||
data.balanceDiff === 0.03 &&
|
data.balanceDiff === 0.02 &&
|
||||||
erc20Balance === '0.01' &&
|
erc20Balance === '0.01' &&
|
||||||
halfDuplexErc20Balance === '0.01' &&
|
|
||||||
investedErc20Balance === '0.01' &&
|
investedErc20Balance === '0.01' &&
|
||||||
accumulatedInterest === '0.001'
|
accumulatedInterest === '0.001'
|
||||||
)
|
)
|
||||||
@ -119,11 +85,10 @@ describe('ERC TO NATIVE with changing state of contracts', () => {
|
|||||||
|
|
||||||
await waitUntil(async () => {
|
await waitUntil(async () => {
|
||||||
;({ data } = await axios.get(`${baseUrl}`))
|
;({ data } = await axios.get(`${baseUrl}`))
|
||||||
const { erc20Balance, halfDuplexErc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
const { erc20Balance, investedErc20Balance, accumulatedInterest } = data.foreign
|
||||||
return (
|
return (
|
||||||
data.balanceDiff === 0.03 &&
|
data.balanceDiff === 0.02 &&
|
||||||
erc20Balance === '0.005' &&
|
erc20Balance === '0.005' &&
|
||||||
halfDuplexErc20Balance === '0.01' &&
|
|
||||||
investedErc20Balance === '0.015' &&
|
investedErc20Balance === '0.015' &&
|
||||||
accumulatedInterest === '0.001'
|
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 initializeChaiToken = async (rpcUrl, bridgeAddress) => {
|
||||||
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
const web3 = new Web3(new Web3.providers.HttpProvider(rpcUrl))
|
||||||
web3.eth.accounts.wallet.add(validator.privateKey)
|
web3.eth.accounts.wallet.add(validator.privateKey)
|
||||||
@ -116,7 +106,6 @@ module.exports = {
|
|||||||
sendTokens,
|
sendTokens,
|
||||||
addValidator,
|
addValidator,
|
||||||
sendAMBMessage,
|
sendAMBMessage,
|
||||||
migrateToMCD,
|
|
||||||
initializeChaiToken,
|
initializeChaiToken,
|
||||||
setMinDaiTokenBalance,
|
setMinDaiTokenBalance,
|
||||||
convertDaiToChai
|
convertDaiToChai
|
||||||
|
@ -21,3 +21,4 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR=1
|
|||||||
|
|
||||||
MONITOR_TX_NUMBER_THRESHOLD=100
|
MONITOR_TX_NUMBER_THRESHOLD=100
|
||||||
MONITOR_PORT=3003
|
MONITOR_PORT=3003
|
||||||
|
MONITOR_CACHE_EVENTS=true
|
||||||
|
@ -3,6 +3,7 @@ const BN = require('bignumber.js')
|
|||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
const logger = require('./logger')('getBalances')
|
const logger = require('./logger')('getBalances')
|
||||||
const { BRIDGE_MODES } = require('../commons')
|
const { BRIDGE_MODES } = require('../commons')
|
||||||
|
const { blockNumberHalfDuplexDisabled } = require('./utils/tokenUtils')
|
||||||
|
|
||||||
const Web3Utils = Web3.utils
|
const Web3Utils = Web3.utils
|
||||||
|
|
||||||
@ -94,7 +95,9 @@ async function main(bridgeMode) {
|
|||||||
if (halfDuplexTokenAddress !== erc20Address) {
|
if (halfDuplexTokenAddress !== erc20Address) {
|
||||||
const halfDuplexToken = new web3Foreign.eth.Contract(ERC20_ABI, halfDuplexTokenAddress)
|
const halfDuplexToken = new web3Foreign.eth.Contract(ERC20_ABI, halfDuplexTokenAddress)
|
||||||
logger.debug('calling halfDuplexToken.methods.balanceOf')
|
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')
|
logger.debug('getting last block numbers')
|
||||||
const block = await web3Foreign.eth.getBlock('latest')
|
const block = await web3Foreign.eth.getBlock('latest')
|
||||||
|
|
||||||
@ -139,8 +142,7 @@ async function main(bridgeMode) {
|
|||||||
const foreignErc20BalanceBN = new BN(foreignErc20Balance)
|
const foreignErc20BalanceBN = new BN(foreignErc20Balance)
|
||||||
const investedAmountInDaiBN = new BN(investedAmountInDai)
|
const investedAmountInDaiBN = new BN(investedAmountInDai)
|
||||||
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)
|
const bridgeDsrBalanceBN = new BN(bridgeDsrBalance)
|
||||||
const halfDuplexErc20BalanceBN =
|
const halfDuplexErc20BalanceBN = displayHalfDuplexToken ? new BN(foreignHalfDuplexErc20Balance) : new BN(0)
|
||||||
displayHalfDuplexToken && tokenSwapAllowed ? new BN(foreignHalfDuplexErc20Balance) : new BN(0)
|
|
||||||
|
|
||||||
const diff = foreignErc20BalanceBN
|
const diff = foreignErc20BalanceBN
|
||||||
.plus(halfDuplexErc20BalanceBN)
|
.plus(halfDuplexErc20BalanceBN)
|
||||||
|
@ -16,12 +16,14 @@ const {
|
|||||||
} = require('../../commons')
|
} = require('../../commons')
|
||||||
const { normalizeEventInformation } = require('./message')
|
const { normalizeEventInformation } = require('./message')
|
||||||
const { filterTransferBeforeES } = require('./tokenUtils')
|
const { filterTransferBeforeES } = require('./tokenUtils')
|
||||||
|
const { writeFile, readCacheFile } = require('./file')
|
||||||
|
|
||||||
const {
|
const {
|
||||||
COMMON_HOME_RPC_URL,
|
COMMON_HOME_RPC_URL,
|
||||||
COMMON_FOREIGN_RPC_URL,
|
COMMON_FOREIGN_RPC_URL,
|
||||||
COMMON_HOME_BRIDGE_ADDRESS,
|
COMMON_HOME_BRIDGE_ADDRESS,
|
||||||
COMMON_FOREIGN_BRIDGE_ADDRESS
|
COMMON_FOREIGN_BRIDGE_ADDRESS,
|
||||||
|
MONITOR_CACHE_EVENTS
|
||||||
} = process.env
|
} = process.env
|
||||||
const MONITOR_HOME_START_BLOCK = toBN(Number(process.env.MONITOR_HOME_START_BLOCK) || 0)
|
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)
|
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 { getBlockNumber } = require('./contract')
|
||||||
|
|
||||||
|
const cacheFilePath = '/tmp/cachedEvents.json'
|
||||||
async function main(mode) {
|
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 homeErcBridge = new web3Home.eth.Contract(HOME_ERC_TO_ERC_ABI, COMMON_HOME_BRIDGE_ADDRESS)
|
||||||
const bridgeMode = mode || (await getBridgeMode(homeErcBridge))
|
const bridgeMode = mode || (await getBridgeMode(homeErcBridge))
|
||||||
const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(bridgeMode)
|
const { HOME_ABI, FOREIGN_ABI } = getBridgeABIs(bridgeMode)
|
||||||
@ -146,11 +158,7 @@ async function main(mode) {
|
|||||||
})).map(normalizeEvent)
|
})).map(normalizeEvent)
|
||||||
|
|
||||||
// Remove events after the ES
|
// Remove events after the ES
|
||||||
const validHalfDuplexTransfers = await filterTransferBeforeES(
|
const validHalfDuplexTransfers = await filterTransferBeforeES(halfDuplexTransferEvents)
|
||||||
halfDuplexTransferEvents,
|
|
||||||
web3Foreign,
|
|
||||||
foreignBridge
|
|
||||||
)
|
|
||||||
|
|
||||||
transferEvents = [...validHalfDuplexTransfers, ...transferEvents]
|
transferEvents = [...validHalfDuplexTransfers, ...transferEvents]
|
||||||
})
|
})
|
||||||
@ -174,7 +182,7 @@ async function main(mode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.debug('Done')
|
logger.debug('Done')
|
||||||
return {
|
const result = {
|
||||||
homeToForeignRequests,
|
homeToForeignRequests,
|
||||||
homeToForeignConfirmations,
|
homeToForeignConfirmations,
|
||||||
foreignToHomeConfirmations,
|
foreignToHomeConfirmations,
|
||||||
@ -182,6 +190,12 @@ async function main(mode) {
|
|||||||
isExternalErc20,
|
isExternalErc20,
|
||||||
bridgeMode
|
bridgeMode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MONITOR_CACHE_EVENTS === 'true') {
|
||||||
|
logger.debug('saving obtained events into cache file')
|
||||||
|
writeFile(cacheFilePath, result, false)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = main
|
module.exports = main
|
||||||
|
@ -15,8 +15,9 @@ async function readFile(filePath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function writeFile(filePath, object) {
|
function writeFile(filePath, object, useCwd = true) {
|
||||||
fs.writeFileSync(path.join(process.cwd(), filePath), JSON.stringify(object, null, 4))
|
const fullPath = useCwd ? path.join(process.cwd(), filePath) : filePath
|
||||||
|
fs.writeFileSync(fullPath, JSON.stringify(object, null, 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDir(dirPath) {
|
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 = {
|
module.exports = {
|
||||||
readFile,
|
readFile,
|
||||||
writeFile,
|
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 bridge stopped supporting half duplex transfers.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
async function transferBeforeES(event, web3Foreign, foreignBridge) {
|
async function transferBeforeES(event) {
|
||||||
const { blockNumber } = event
|
return event.blockNumber < blockNumberHalfDuplexDisabled
|
||||||
|
|
||||||
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 filterTransferBeforeES(array, web3Foreign, foreignBridge) {
|
async function filterTransferBeforeES(array) {
|
||||||
const newArray = []
|
const newArray = []
|
||||||
// Iterate events from newer to older
|
// Iterate events from newer to older
|
||||||
for (let i = array.length - 1; i >= 0; i--) {
|
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) {
|
if (beforeES) {
|
||||||
// add element to first position so the new array will have the same order
|
// add element to first position so the new array will have the same order
|
||||||
newArray.unshift(array[i])
|
newArray.unshift(array[i])
|
||||||
@ -36,5 +22,6 @@ async function filterTransferBeforeES(array, web3Foreign, foreignBridge) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
filterTransferBeforeES
|
filterTransferBeforeES,
|
||||||
|
blockNumberHalfDuplexDisabled
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ const {
|
|||||||
homeRPC,
|
homeRPC,
|
||||||
foreignRPC
|
foreignRPC
|
||||||
} = require('../../e2e-commons/constants.json')
|
} = 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 { ERC677_BRIDGE_TOKEN_ABI, FOREIGN_ERC_TO_NATIVE_ABI, HOME_ERC_TO_NATIVE_ABI } = require('../../commons')
|
||||||
const { uniformRetry, sleep } = require('../../e2e-commons/utils')
|
const { uniformRetry } = require('../../e2e-commons/utils')
|
||||||
const { setRequiredSignatures } = require('./utils')
|
const { setRequiredSignatures } = require('./utils')
|
||||||
|
|
||||||
const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL))
|
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 originalBalanceOnHome = await homeWeb3.eth.getBalance(user.address)
|
||||||
|
|
||||||
const transferValue = homeWeb3.utils.toWei('0.01')
|
const transferValue = homeWeb3.utils.toWei('0.01')
|
||||||
|
|
||||||
// erc20 token address and half duplex address are the same before migration
|
// send tokens to foreign bridge
|
||||||
const tokenAddress = await foreignBridge.methods.erc20token().call()
|
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
|
// send tokens to foreign bridge
|
||||||
await erc20AndhalfDuplexToken.methods
|
await erc20Token.methods
|
||||||
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
.transfer(COMMON_FOREIGN_BRIDGE_ADDRESS, transferValue)
|
||||||
.send({
|
.send({
|
||||||
from: user.address,
|
from: user.address,
|
||||||
@ -94,19 +111,7 @@ describe('erc to native', () => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// call migration
|
const afterTransferBalance = await homeWeb3.eth.getBalance(user.address)
|
||||||
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)
|
|
||||||
|
|
||||||
// send tokens to foreign bridge
|
// send tokens to foreign bridge
|
||||||
await erc20Token.methods
|
await erc20Token.methods
|
||||||
@ -123,39 +128,12 @@ describe('erc to native', () => {
|
|||||||
await promiseRetry(async (retry, number) => {
|
await promiseRetry(async (retry, number) => {
|
||||||
const balance = await homeWeb3.eth.getBalance(user.address)
|
const balance = await homeWeb3.eth.getBalance(user.address)
|
||||||
// retry at least 4 times to check transfer is not double processed by the two watchers
|
// 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()
|
retry()
|
||||||
} else {
|
} else {
|
||||||
assert(
|
assert(
|
||||||
toBN(balance).eq(toBN(AfterMigrateBalance).add(toBN(transferValue))),
|
toBN(balance).eq(toBN(afterTransferBalance).add(toBN(transferValue))),
|
||||||
'User balance should be increased only by second transfer'
|
'User balance should be increased'
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
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'
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -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 () => {
|
it('should convert coins in home to tokens in foreign', async () => {
|
||||||
const originalBalance = await erc20Token.methods.balanceOf(user.address).call()
|
const originalBalance = await erc20Token.methods.balanceOf(user.address).call()
|
||||||
|
|
||||||
|
@ -12,10 +12,8 @@
|
|||||||
"eslint": "5.16.0",
|
"eslint": "5.16.0",
|
||||||
"eslint-config-airbnb": "17.1.0",
|
"eslint-config-airbnb": "17.1.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.2.1",
|
"eslint-plugin-jsx-a11y": "^6.2.1",
|
||||||
"eslint-plugin-import": "2.14.0",
|
|
||||||
"eslint-plugin-node": "7.0.1",
|
"eslint-plugin-node": "7.0.1",
|
||||||
"eslint-plugin-prettier": "2.6.2",
|
"eslint-plugin-prettier": "2.6.2",
|
||||||
"eslint-config-react-app": "^4.0.1",
|
|
||||||
"eslint-plugin-react": "^7.13.0",
|
"eslint-plugin-react": "^7.13.0",
|
||||||
"eslint-plugin-react-hooks": "^1.6.0",
|
"eslint-plugin-react-hooks": "^1.6.0",
|
||||||
"eslint-plugin-flowtype": "^3.8.1",
|
"eslint-plugin-flowtype": "^3.8.1",
|
||||||
@ -30,16 +28,18 @@
|
|||||||
"ui-e2e",
|
"ui-e2e",
|
||||||
"monitor",
|
"monitor",
|
||||||
"monitor-e2e",
|
"monitor-e2e",
|
||||||
"contracts"
|
"contracts",
|
||||||
|
"burner-wallet-plugin"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"initialize": "yarn clean && git submodule update --init && yarn install --unsafe-perm --frozen-lockfile && yarn install:deploy && yarn compile:contracts",
|
"initialize": "yarn clean && git submodule update --init && yarn install --unsafe-perm --frozen-lockfile && yarn install:deploy && yarn compile:contracts",
|
||||||
"build": "yarn workspace ui run build",
|
"build": "yarn workspace ui run build",
|
||||||
|
"build:plugin": "yarn workspace burner-wallet-plugin run build",
|
||||||
"lint": "yarn wsrun --exclude token-bridge-contracts lint",
|
"lint": "yarn wsrun --exclude token-bridge-contracts lint",
|
||||||
"test": "yarn wsrun --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e test",
|
"test": "yarn wsrun --exclude oracle-e2e --exclude ui-e2e --exclude monitor-e2e test",
|
||||||
"oracle-e2e": "./oracle-e2e/run-tests.sh",
|
"oracle-e2e": "./oracle-e2e/run-tests.sh",
|
||||||
"ui-e2e": "./ui-e2e/run-tests.sh",
|
"ui-e2e": "./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",
|
"compile:contracts": "yarn workspace token-bridge-contracts run compile",
|
||||||
"install:deploy": "cd contracts/deploy && npm install --unsafe-perm --silent",
|
"install:deploy": "cd contracts/deploy && npm install --unsafe-perm --silent",
|
||||||
"postinstall": "ln -sf $(pwd)/node_modules/openzeppelin-solidity/ contracts/node_modules/openzeppelin-solidity"
|
"postinstall": "ln -sf $(pwd)/node_modules/openzeppelin-solidity/ contracts/node_modules/openzeppelin-solidity"
|
||||||
|
Loading…
Reference in New Issue
Block a user