Add Burner Wallet Plugin for WETC Bridge (#306)
This commit is contained in:
parent
64c5a5670f
commit
738442e4cf
@ -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
|
||||||
|
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
@ -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