Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffd88f6cd0 | ||
|
|
caf2e2b4d3 | ||
|
|
d5d0c8f56a | ||
|
|
dc27bd6caa | ||
|
|
ab814f831c | ||
|
|
691e4294ae | ||
|
|
4a727dc159 | ||
|
|
2ca07e998a | ||
|
|
0eb7c41278 | ||
|
|
9e9e891db8 | ||
|
|
d228bb7ea9 | ||
|
|
d2606997a3 | ||
|
|
3c956ab9ec | ||
|
|
9b3e6a51a9 | ||
|
|
d512b9850f | ||
|
|
bcdf691000 | ||
|
|
8e10a5d609 | ||
|
|
da60edae4b | ||
|
|
e7eb8ec758 | ||
|
|
571dec8022 | ||
|
|
4db62d721d | ||
|
|
8d6acd0339 | ||
|
|
c013cc7378 | ||
|
|
d6e39f34af | ||
|
|
3b368ce644 | ||
|
|
c1d58c2908 | ||
|
|
d17e9e0eea | ||
|
|
a2c678d0a2 | ||
|
|
4bd3576691 | ||
|
|
d3576f5a79 | ||
|
|
62f9a080c9 | ||
|
|
10f67168a7 | ||
|
|
f90f888ae4 | ||
|
|
84508e2b84 | ||
|
|
2369e876aa |
@@ -314,3 +314,8 @@ workflows:
|
||||
scenario-name: amb
|
||||
redis-key: amb-collected-signatures:lastProcessedBlock
|
||||
oracle-e2e-script: "amb"
|
||||
- ultimate:
|
||||
name: "ultimate: amb stake erc to erc"
|
||||
scenario-name: ultimate-amb-stake-erc-to-erc
|
||||
redis-key: amb-collected-signatures:lastProcessedBlock
|
||||
ui-e2e-grep: "AMB-STAKE-ERC-TO-ERC"
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
**/*.md
|
||||
|
||||
contracts/test
|
||||
contracts/build
|
||||
oracle/test
|
||||
oracle/**/*.png
|
||||
oracle/**/*.jpg
|
||||
audit
|
||||
|
||||
@@ -3,3 +3,4 @@ submodules
|
||||
coverage
|
||||
lib
|
||||
dist
|
||||
build
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -49,4 +49,5 @@ __pycache__
|
||||
|
||||
#monitor
|
||||
monitor/responses/*
|
||||
monitor/configs/*.env
|
||||
!monitor/.gitkeep
|
||||
|
||||
@@ -43,6 +43,7 @@ ORACLE_VALIDATOR_ADDRESS | The public address of the bridge validator | hexideci
|
||||
name | description | value
|
||||
--- | --- | ---
|
||||
UI_TITLE | The title for the bridge UI page. `%c` will be replaced by the name of the network. | string
|
||||
UI_OG_TITLE | The meta title for the deployed bridge page. | string
|
||||
UI_DESCRIPTION | The meta description for the deployed bridge page. | string
|
||||
UI_NATIVE_TOKEN_DISPLAY_NAME | name of the home native coin | string
|
||||
UI_HOME_NETWORK_DISPLAY_NAME | name to be displayed for home network | string
|
||||
@@ -56,7 +57,8 @@ UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE | template link to address on foreign explo
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Home Bridge contract. | integer
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL | An interval in milliseconds used to get the updated gas price value either from the oracle or from the Foreign Bridge contract. | integer
|
||||
UI_PORT | The port for the UI app. | integer
|
||||
UI_STYLES | The set of styles to render the bridge UI page. Currently only `classic` is implemented | classic
|
||||
UI_STYLES | The set of styles to render the bridge UI page. | core/classic/stake
|
||||
UI_PUBLIC_URL | The public url for the deployed bridge page | string
|
||||
|
||||
|
||||
## Monitor configuration
|
||||
|
||||
@@ -29,6 +29,8 @@ Sub-repositories maintained within this monorepo are listed below.
|
||||
| [Deployment-E2E](deployment-e2e/README.md) | End to end tests for the Deployment |
|
||||
| [Commons](commons/README.md) | Interfaces, constants and utilities shared between the sub-repositories |
|
||||
| [E2E-Commons](e2e-commons/README.md) | Common utilities and configuration used in end to end tests |
|
||||
| [ALM](alm/README.md) | DApp interface tool for AMB Live Monitoring |
|
||||
| [Burner-wallet-plugin](burner-wallet-plugin/README.md) | TokenBridge Burner Wallet 2 Plugin |
|
||||
|
||||
Additionally there are [Smart Contracts](https://github.com/poanetwork/tokenbridge-contracts) used to manage bridge validators, collect signatures, and confirm asset relay and disposal.
|
||||
|
||||
|
||||
15
alm/.env.example
Normal file
15
alm/.env.example
Normal file
@@ -0,0 +1,15 @@
|
||||
COMMON_HOME_BRIDGE_ADDRESS=0xFe446bEF1DbF7AFE24E81e05BC8B271C1BA9a560
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS=0xFe446bEF1DbF7AFE24E81e05BC8B271C1BA9a560
|
||||
|
||||
COMMON_HOME_RPC_URL=https://sokol.poa.network
|
||||
COMMON_FOREIGN_RPC_URL=https://kovan.infura.io/v3/
|
||||
|
||||
ALM_HOME_NETWORK_NAME=Sokol Testnet
|
||||
ALM_FOREIGN_NETWORK_NAME=Kovan Testnet
|
||||
|
||||
ALM_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx/%s
|
||||
ALM_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s
|
||||
|
||||
ALM_HOME_EXPLORER_API=https://blockscout.com/poa/sokol/api
|
||||
ALM_FOREIGN_EXPLORER_API=https://kovan.etherscan.io/api?apikey=YourApiKeyToken
|
||||
PORT=8080
|
||||
6
alm/.eslintrc.js
Normal file
6
alm/.eslintrc.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
"react-app",
|
||||
"../.eslintrc"
|
||||
]
|
||||
}
|
||||
23
alm/.gitignore
vendored
Normal file
23
alm/.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
29
alm/Dockerfile
Normal file
29
alm/Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
||||
FROM node:12 as alm-builder
|
||||
|
||||
WORKDIR /mono
|
||||
COPY package.json .
|
||||
COPY contracts/package.json ./contracts/
|
||||
COPY commons/package.json ./commons/
|
||||
COPY alm/package.json ./alm/
|
||||
COPY yarn.lock .
|
||||
RUN yarn install --production --frozen-lockfile
|
||||
|
||||
COPY ./contracts ./contracts
|
||||
RUN yarn run compile:contracts
|
||||
RUN mv ./contracts/build ./ && rm -rf ./contracts/* ./contracts/.[!.]* && mv ./build ./contracts/
|
||||
|
||||
COPY ./commons ./commons
|
||||
|
||||
COPY ./alm ./alm
|
||||
ARG DOT_ENV_PATH=./alm/.env
|
||||
COPY ${DOT_ENV_PATH} ./alm/.env
|
||||
|
||||
WORKDIR /mono/alm
|
||||
RUN yarn run build
|
||||
|
||||
|
||||
FROM node:12 as alm-production
|
||||
RUN yarn global add serve
|
||||
WORKDIR /app
|
||||
COPY --from=alm-builder /mono/alm/build .
|
||||
CMD serve -p $PORT -s .
|
||||
46
alm/README.md
Normal file
46
alm/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# AMB Live Monitoring
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `yarn start`
|
||||
|
||||
Runs the app in the development mode.<br />
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.<br />
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
### `yarn test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.<br />
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `yarn build`
|
||||
|
||||
Builds the app for production to the `build` folder.<br />
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.<br />
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `yarn eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
9
alm/config-overrides.js
Normal file
9
alm/config-overrides.js
Normal file
@@ -0,0 +1,9 @@
|
||||
const { override, disableEsLint } = require('customize-cra')
|
||||
const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
|
||||
|
||||
const disableModuleScopePlugin = () => config => {
|
||||
config.resolve.plugins = config.resolve.plugins.filter(plugin => !(plugin instanceof ModuleScopePlugin))
|
||||
return config
|
||||
}
|
||||
|
||||
module.exports = override(disableEsLint(), disableModuleScopePlugin())
|
||||
14
alm/docker-compose.yml
Normal file
14
alm/docker-compose.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
version: '2.4'
|
||||
services:
|
||||
alm:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: alm/Dockerfile
|
||||
ports:
|
||||
- "${PORT}:${PORT}"
|
||||
env_file: ./.env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
restart: unless-stopped
|
||||
entrypoint: serve -p ${PORT} -s .
|
||||
16
alm/load-env.sh
Executable file
16
alm/load-env.sh
Executable file
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
while read line; do
|
||||
if [ "$line" = "" ]; then
|
||||
: # Skip empty lines
|
||||
elif [[ "$line" =~ \#.* ]]; then
|
||||
: # Skip comment lines
|
||||
elif [[ "$line" =~ "UI_PORT"* ]]; then
|
||||
eval $line
|
||||
export PORT="$UI_PORT"
|
||||
else
|
||||
export "REACT_APP_$line"
|
||||
fi
|
||||
done < '.env'
|
||||
|
||||
$*
|
||||
57
alm/package.json
Normal file
57
alm/package.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"name": "alm",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@react-hook/window-size": "^3.0.6",
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.3.2",
|
||||
"@testing-library/user-event": "^7.1.2",
|
||||
"@types/jest": "^24.0.0",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/promise-retry": "^1.1.3",
|
||||
"@types/react": "^16.9.0",
|
||||
"@types/react-dom": "^16.9.0",
|
||||
"@types/react-router-dom": "^5.1.5",
|
||||
"@types/styled-components": "^5.1.0",
|
||||
"@use-it/interval": "^0.1.3",
|
||||
"customize-cra": "^1.0.0",
|
||||
"date-fns": "^2.14.0",
|
||||
"fast-memoize": "^2.5.2",
|
||||
"promise-retry": "^2.0.1",
|
||||
"react": "^16.13.1",
|
||||
"react-app-rewired": "^2.1.6",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "3.0.1",
|
||||
"styled-components": "^5.1.1",
|
||||
"typescript": "^3.5.2",
|
||||
"web3": "1.2.7",
|
||||
"web3-eth-contract": "1.2.7"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "./load-env.sh react-app-rewired start",
|
||||
"build": "./load-env.sh react-app-rewired build",
|
||||
"test": "react-app-rewired test",
|
||||
"eject": "react-app-rewired eject",
|
||||
"lint": "eslint '*/**/*.{js,ts,tsx}' --ignore-path ../.eslintignore"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-plugin-prettier": "^3.1.3"
|
||||
}
|
||||
}
|
||||
BIN
alm/public/favicon.ico
Normal file
BIN
alm/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
45
alm/public/index.html
Normal file
45
alm/public/index.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="AMB Live Monitoring"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://unpkg.com/chota@latest">
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
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`.
|
||||
-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,700" rel="stylesheet">
|
||||
<title>AMB Live Monitoring</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
alm/public/manifest.json
Normal file
15
alm/public/manifest.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "ALM",
|
||||
"name": "AMB Live Monitoring",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
alm/public/robots.txt
Normal file
3
alm/public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
5
alm/src/App.test.tsx
Normal file
5
alm/src/App.test.tsx
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react'
|
||||
|
||||
test('renders learn react link', () => {
|
||||
// Removed basic test from setup. Keeping this so CI passes until we add unit tests.
|
||||
})
|
||||
16
alm/src/App.tsx
Normal file
16
alm/src/App.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import { MainPage } from './components/MainPage'
|
||||
import { StateProvider } from './state/StateProvider'
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<StateProvider>
|
||||
<MainPage />
|
||||
</StateProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
319
alm/src/abis/BridgeValidators.ts
Normal file
319
alm/src/abis/BridgeValidators.ts
Normal file
@@ -0,0 +1,319 @@
|
||||
import { AbiItem } from 'web3-utils'
|
||||
|
||||
const abi: AbiItem[] = [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'validatorCount',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'getBridgeValidatorsInterfacesVersion',
|
||||
outputs: [
|
||||
{
|
||||
name: 'major',
|
||||
type: 'uint64'
|
||||
},
|
||||
{
|
||||
name: 'minor',
|
||||
type: 'uint64'
|
||||
},
|
||||
{
|
||||
name: 'patch',
|
||||
type: 'uint64'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'pure',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'isInitialized',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'validatorList',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address[]'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_requiredSignatures',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'setRequiredSignatures',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'requiredSignatures',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_address',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'getNextValidator',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'owner',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_validator',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'isValidatorDuty',
|
||||
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: 'F_ADDR',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'newOwner',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'transferOwnership',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_validator',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'isValidator',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'validator',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'ValidatorAdded',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'validator',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'ValidatorRemoved',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'requiredSignatures',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'RequiredSignaturesChanged',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'previousOwner',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'newOwner',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'OwnershipTransferred',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_requiredSignatures',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_initialValidators',
|
||||
type: 'address[]'
|
||||
},
|
||||
{
|
||||
name: '_owner',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'initialize',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_validator',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'addValidator',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_validator',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'removeValidator',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
export default abi
|
||||
589
alm/src/abis/ForeignAMB.ts
Normal file
589
alm/src/abis/ForeignAMB.ts
Normal file
@@ -0,0 +1,589 @@
|
||||
import { AbiItem } from 'web3-utils'
|
||||
|
||||
const abi: AbiItem[] = [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'transactionHash',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_txHash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'relayedMessages',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_sourceChainId',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_destinationChainId',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_validatorContract',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_maxGasPerTx',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_gasPrice',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_requiredBlockConfirmations',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_owner',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'initialize',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'isInitialized',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'requiredBlockConfirmations',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_data',
|
||||
type: 'bytes'
|
||||
},
|
||||
{
|
||||
name: '_signatures',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'executeSignatures',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_data',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'getMinimumGasUsage',
|
||||
outputs: [
|
||||
{
|
||||
name: 'gas',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'pure',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_messageId',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'failedMessageReceiver',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'getBridgeMode',
|
||||
outputs: [
|
||||
{
|
||||
name: '_data',
|
||||
type: 'bytes4'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'pure',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_sourceChainId',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_destinationChainId',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'setChainIds',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_messageId',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'failedMessageSender',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'messageId',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_token',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_to',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'claimTokens',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_maxGasPerTx',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'setMaxGasPerTx',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'requiredSignatures',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'owner',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'validatorContract',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
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: 'getBridgeInterfacesVersion',
|
||||
outputs: [
|
||||
{
|
||||
name: 'major',
|
||||
type: 'uint64'
|
||||
},
|
||||
{
|
||||
name: 'minor',
|
||||
type: 'uint64'
|
||||
},
|
||||
{
|
||||
name: 'patch',
|
||||
type: 'uint64'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'pure',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'messageSourceChainId',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_blockConfirmations',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'setRequiredBlockConfirmations',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_gasPrice',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'setGasPrice',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_messageId',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'messageCallStatus',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'messageSender',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_contract',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_data',
|
||||
type: 'bytes'
|
||||
},
|
||||
{
|
||||
name: '_gas',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'requireToPassMessage',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_messageId',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'failedMessageDataHash',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'maxGasPerTx',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'newOwner',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'transferOwnership',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'gasPrice',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'messageId',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'encodedData',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'UserRequestForAffirmation',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'sender',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: 'executor',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: 'messageId',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'status',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
name: 'RelayedMessage',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'gasPrice',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'GasPriceChanged',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'requiredBlockConfirmations',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'RequiredBlockConfirmationChanged',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'previousOwner',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'newOwner',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'OwnershipTransferred',
|
||||
type: 'event'
|
||||
}
|
||||
]
|
||||
|
||||
export default abi
|
||||
777
alm/src/abis/HomeAMB.ts
Normal file
777
alm/src/abis/HomeAMB.ts
Normal file
@@ -0,0 +1,777 @@
|
||||
import { AbiItem } from 'web3-utils'
|
||||
|
||||
const abi: AbiItem[] = [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'transactionHash',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_message',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'numMessagesSigned',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_hash',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
name: '_index',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'signature',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_sourceChainId',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_destinationChainId',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_validatorContract',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_maxGasPerTx',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_gasPrice',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_requiredBlockConfirmations',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_owner',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'initialize',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'isInitialized',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'requiredBlockConfirmations',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_data',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'getMinimumGasUsage',
|
||||
outputs: [
|
||||
{
|
||||
name: 'gas',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'pure',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_messageId',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'failedMessageReceiver',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'getBridgeMode',
|
||||
outputs: [
|
||||
{
|
||||
name: '_data',
|
||||
type: 'bytes4'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'pure',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_sourceChainId',
|
||||
type: 'uint256'
|
||||
},
|
||||
{
|
||||
name: '_destinationChainId',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'setChainIds',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_hash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'message',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_messageId',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'failedMessageSender',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'signature',
|
||||
type: 'bytes'
|
||||
},
|
||||
{
|
||||
name: 'message',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'submitSignature',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'messageId',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_token',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_to',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'claimTokens',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_hash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'numAffirmationsSigned',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_hash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'affirmationsSigned',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_maxGasPerTx',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'setMaxGasPerTx',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'requiredSignatures',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'owner',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_message',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'messagesSigned',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'validatorContract',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
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: 'getBridgeInterfacesVersion',
|
||||
outputs: [
|
||||
{
|
||||
name: 'major',
|
||||
type: 'uint64'
|
||||
},
|
||||
{
|
||||
name: 'minor',
|
||||
type: 'uint64'
|
||||
},
|
||||
{
|
||||
name: 'patch',
|
||||
type: 'uint64'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'pure',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'messageSourceChainId',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_blockConfirmations',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'setRequiredBlockConfirmations',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_gasPrice',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'setGasPrice',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_messageId',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'messageCallStatus',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'messageSender',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '_contract',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '_data',
|
||||
type: 'bytes'
|
||||
},
|
||||
{
|
||||
name: '_gas',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'requireToPassMessage',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_messageId',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'failedMessageDataHash',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'maxGasPerTx',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'message',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'executeAffirmation',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: 'newOwner',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'transferOwnership',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [],
|
||||
name: 'gasPrice',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '_number',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'isAlreadyProcessed',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'pure',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'messageId',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'encodedData',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'UserRequestForSignature',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'sender',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: 'executor',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: true,
|
||||
name: 'messageId',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'status',
|
||||
type: 'bool'
|
||||
}
|
||||
],
|
||||
name: 'AffirmationCompleted',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'signer',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'messageHash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'SignedForUserRequest',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: true,
|
||||
name: 'signer',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'messageHash',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'SignedForAffirmation',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'authorityResponsibleForRelay',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'messageHash',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'NumberOfCollectedSignatures',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'CollectedSignatures',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'gasPrice',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'GasPriceChanged',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'requiredBlockConfirmations',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'RequiredBlockConfirmationChanged',
|
||||
type: 'event'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'previousOwner',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
indexed: false,
|
||||
name: 'newOwner',
|
||||
type: 'address'
|
||||
}
|
||||
],
|
||||
name: 'OwnershipTransferred',
|
||||
type: 'event'
|
||||
}
|
||||
]
|
||||
|
||||
export default abi
|
||||
3
alm/src/abis/index.ts
Normal file
3
alm/src/abis/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export { default as HOME_AMB_ABI } from './HomeAMB'
|
||||
export { default as FOREIGN_AMB_ABI } from './ForeignAMB'
|
||||
export { default as BRIDGE_VALIDATORS_ABI } from './BridgeValidators'
|
||||
85
alm/src/components/ConfirmationsContainer.tsx
Normal file
85
alm/src/components/ConfirmationsContainer.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import React from 'react'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { useMessageConfirmations } from '../hooks/useMessageConfirmations'
|
||||
import { MessageObject } from '../utils/web3'
|
||||
import styled from 'styled-components'
|
||||
import { CONFIRMATIONS_STATUS } from '../config/constants'
|
||||
import { CONFIRMATIONS_STATUS_LABEL } from '../config/descriptions'
|
||||
import { SimpleLoading } from './commons/Loading'
|
||||
import { ValidatorsConfirmations } from './ValidatorsConfirmations'
|
||||
import { getConfirmationsStatusDescription } from '../utils/networks'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { ExecutionConfirmation } from './ExecutionConfirmation'
|
||||
import { useValidatorContract } from '../hooks/useValidatorContract'
|
||||
import { useBlockConfirmations } from '../hooks/useBlockConfirmations'
|
||||
|
||||
const StatusLabel = styled.label`
|
||||
font-weight: bold;
|
||||
font-size: 18px;
|
||||
`
|
||||
|
||||
const StatusResultLabel = styled.label`
|
||||
font-size: 18px;
|
||||
padding-left: 10px;
|
||||
`
|
||||
|
||||
const StyledConfirmationContainer = styled.div`
|
||||
background-color: var(--bg-color);
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
`
|
||||
|
||||
const StatusDescription = styled.div`
|
||||
padding-top: 10px;
|
||||
`
|
||||
|
||||
export interface ConfirmationsContainerParams {
|
||||
message: MessageObject
|
||||
receipt: Maybe<TransactionReceipt>
|
||||
fromHome: boolean
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
export const ConfirmationsContainer = ({ message, receipt, fromHome, timestamp }: ConfirmationsContainerParams) => {
|
||||
const {
|
||||
home: { name: homeName },
|
||||
foreign: { name: foreignName }
|
||||
} = useStateProvider()
|
||||
const { requiredSignatures, validatorList } = useValidatorContract({ fromHome, receipt })
|
||||
const { blockConfirmations } = useBlockConfirmations({ fromHome, receipt })
|
||||
const { confirmations, status, executionData, signatureCollected } = useMessageConfirmations({
|
||||
message,
|
||||
receipt,
|
||||
fromHome,
|
||||
timestamp,
|
||||
requiredSignatures,
|
||||
validatorList,
|
||||
blockConfirmations
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="row is-center">
|
||||
<StyledConfirmationContainer className="col-9">
|
||||
<div className="row is-center">
|
||||
<StatusLabel>Status:</StatusLabel>
|
||||
<StatusResultLabel>
|
||||
{status !== CONFIRMATIONS_STATUS.UNDEFINED ? CONFIRMATIONS_STATUS_LABEL[status] : <SimpleLoading />}
|
||||
</StatusResultLabel>
|
||||
</div>
|
||||
<StatusDescription className="row is-center">
|
||||
<p className="col-10">
|
||||
{status !== CONFIRMATIONS_STATUS.UNDEFINED
|
||||
? getConfirmationsStatusDescription(status, homeName, foreignName)
|
||||
: ''}
|
||||
</p>
|
||||
</StatusDescription>
|
||||
<ValidatorsConfirmations
|
||||
confirmations={confirmations}
|
||||
requiredSignatures={requiredSignatures}
|
||||
validatorList={validatorList}
|
||||
/>
|
||||
{signatureCollected && <ExecutionConfirmation executionData={executionData} isHome={!fromHome} />}
|
||||
</StyledConfirmationContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
69
alm/src/components/ExecutionConfirmation.tsx
Normal file
69
alm/src/components/ExecutionConfirmation.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
import React from 'react'
|
||||
import { formatTimestamp, formatTxHash, getExplorerTxUrl } from '../utils/networks'
|
||||
import { useWindowWidth } from '@react-hook/window-size'
|
||||
import { VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import { SimpleLoading } from './commons/Loading'
|
||||
import styled from 'styled-components'
|
||||
import { ExecutionData } from '../hooks/useMessageConfirmations'
|
||||
import { GreyLabel, RedLabel, SuccessLabel } from './commons/Labels'
|
||||
import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
||||
|
||||
const Thead = styled.thead`
|
||||
border-bottom: 2px solid #9e9e9e;
|
||||
`
|
||||
|
||||
const StyledExecutionConfirmation = styled.div`
|
||||
margin-top: 30px;
|
||||
`
|
||||
|
||||
export interface ExecutionConfirmationParams {
|
||||
executionData: ExecutionData
|
||||
isHome: boolean
|
||||
}
|
||||
|
||||
export const ExecutionConfirmation = ({ executionData, isHome }: ExecutionConfirmationParams) => {
|
||||
const windowWidth = useWindowWidth()
|
||||
|
||||
const txExplorerLink = getExplorerTxUrl(executionData.txHash, isHome)
|
||||
const formattedValidator =
|
||||
windowWidth < 850 && executionData.validator ? formatTxHash(executionData.validator) : executionData.validator
|
||||
|
||||
const getExecutionStatusElement = (validatorStatus = '') => {
|
||||
switch (validatorStatus) {
|
||||
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
|
||||
return <SuccessLabel>{validatorStatus}</SuccessLabel>
|
||||
case VALIDATOR_CONFIRMATION_STATUS.FAILED:
|
||||
return <RedLabel>{validatorStatus}</RedLabel>
|
||||
case VALIDATOR_CONFIRMATION_STATUS.PENDING:
|
||||
case VALIDATOR_CONFIRMATION_STATUS.WAITING:
|
||||
return <GreyLabel>{validatorStatus}</GreyLabel>
|
||||
default:
|
||||
return <SimpleLoading />
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledExecutionConfirmation>
|
||||
<table>
|
||||
<Thead>
|
||||
<tr>
|
||||
<th>Executed by</th>
|
||||
<th className="text-center">Status</th>
|
||||
<th className="text-center">Age</th>
|
||||
</tr>
|
||||
</Thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{formattedValidator ? formattedValidator : <SimpleLoading />}</td>
|
||||
<td className="text-center">{getExecutionStatusElement(executionData.status)}</td>
|
||||
<td className="text-center">
|
||||
<ExplorerTxLink href={txExplorerLink} target="_blank">
|
||||
{executionData.timestamp > 0 ? formatTimestamp(executionData.timestamp) : ''}
|
||||
</ExplorerTxLink>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</StyledExecutionConfirmation>
|
||||
)
|
||||
}
|
||||
64
alm/src/components/Form.tsx
Normal file
64
alm/src/components/Form.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import React, { useState, FormEvent } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { FormSubmitParams } from './MainPage'
|
||||
import { Button } from './commons/Button'
|
||||
import { TransactionSelector } from './TransactionSelector'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
|
||||
const LabelText = styled.label`
|
||||
line-height: 36px;
|
||||
max-width: 140px;
|
||||
`
|
||||
|
||||
const Input = styled.input`
|
||||
background-color: var(--bg-color);
|
||||
color: var(--font-color);
|
||||
max-width: 100%;
|
||||
border-color: var(--color-primary) !important;
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
border-color: var(--button-color) !important;
|
||||
}
|
||||
`
|
||||
|
||||
export const Form = ({ onSubmit }: { onSubmit: ({ chainId, txHash, receipt }: FormSubmitParams) => void }) => {
|
||||
const [txHash, setTxHash] = useState('')
|
||||
const [searchTx, setSearchTx] = useState(false)
|
||||
|
||||
const formSubmit = (e: FormEvent) => {
|
||||
e.preventDefault()
|
||||
setSearchTx(true)
|
||||
}
|
||||
|
||||
const onSelected = (chainId: number, receipt: TransactionReceipt) => {
|
||||
onSubmit({ chainId, txHash, receipt })
|
||||
}
|
||||
|
||||
if (searchTx) {
|
||||
return <TransactionSelector txHash={txHash} onSelected={onSelected} />
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={formSubmit}>
|
||||
<div className="row is-center">
|
||||
<LabelText className="col-2">Bridgeable tx hash:</LabelText>
|
||||
<div className="col-7">
|
||||
<Input
|
||||
placeholder="Enter transaction hash"
|
||||
type="text"
|
||||
onChange={e => setTxHash(e.target.value)}
|
||||
required
|
||||
pattern="^0x[a-fA-F0-9]{64}$"
|
||||
value={txHash}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-1">
|
||||
<Button className="button outline" type="submit">
|
||||
Check
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
90
alm/src/components/MainPage.tsx
Normal file
90
alm/src/components/MainPage.tsx
Normal file
@@ -0,0 +1,90 @@
|
||||
import React, { useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { Route, useHistory } from 'react-router-dom'
|
||||
import { Form } from './Form'
|
||||
import { StatusContainer } from './StatusContainer'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
|
||||
const StyledMainPage = styled.div`
|
||||
text-align: center;
|
||||
min-height: 100vh;
|
||||
`
|
||||
|
||||
const Header = styled.header`
|
||||
background-color: #001529;
|
||||
color: #ffffff;
|
||||
margin-bottom: 50px;
|
||||
`
|
||||
|
||||
const HeaderContainer = styled.header`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 16px;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
padding: 0 50px;
|
||||
|
||||
@media (max-width: 600px) {
|
||||
padding: 0 20px;
|
||||
}
|
||||
`
|
||||
|
||||
export interface FormSubmitParams {
|
||||
chainId: number
|
||||
txHash: string
|
||||
receipt: TransactionReceipt
|
||||
}
|
||||
|
||||
export const MainPage = () => {
|
||||
const history = useHistory()
|
||||
const { home, foreign } = useStateProvider()
|
||||
const [networkName, setNetworkName] = useState('')
|
||||
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
|
||||
|
||||
const setNetworkData = (chainId: number) => {
|
||||
const network = chainId === home.chainId ? home.name : foreign.name
|
||||
|
||||
setNetworkName(network)
|
||||
}
|
||||
|
||||
const onFormSubmit = ({ chainId, txHash, receipt }: FormSubmitParams) => {
|
||||
setNetworkData(chainId)
|
||||
setReceipt(receipt)
|
||||
history.push(`/${chainId}/${txHash}`)
|
||||
}
|
||||
|
||||
const resetNetworkHeader = () => {
|
||||
setNetworkName('')
|
||||
}
|
||||
|
||||
const setNetworkFromParams = (chainId: number) => {
|
||||
setNetworkData(chainId)
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledMainPage>
|
||||
<Header>
|
||||
<HeaderContainer>
|
||||
<span>AMB Live Monitoring</span>
|
||||
<span>{networkName}</span>
|
||||
</HeaderContainer>
|
||||
</Header>
|
||||
<div className="container">
|
||||
<Route exact path={['/']} children={<Form onSubmit={onFormSubmit} />} />
|
||||
<Route
|
||||
path={['/:chainId/:txHash/:messageIdParam', '/:chainId/:txHash']}
|
||||
children={
|
||||
<StatusContainer
|
||||
onBackToMain={resetNetworkHeader}
|
||||
setNetworkFromParams={setNetworkFromParams}
|
||||
receiptParam={receipt}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</StyledMainPage>
|
||||
)
|
||||
}
|
||||
47
alm/src/components/MessageSelector.tsx
Normal file
47
alm/src/components/MessageSelector.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Button } from './commons/Button'
|
||||
import { RadioButtonLabel, RadioButtonContainer } from './commons/RadioButton'
|
||||
import { useWindowWidth } from '@react-hook/window-size'
|
||||
import { formatTxHashExtended } from '../utils/networks'
|
||||
import { MessageObject } from '../utils/web3'
|
||||
|
||||
export interface MessageSelectorParams {
|
||||
messages: Array<MessageObject>
|
||||
onMessageSelected: (index: number) => void
|
||||
}
|
||||
|
||||
export const MessageSelector = ({ messages, onMessageSelected }: MessageSelectorParams) => {
|
||||
const [messageIndex, setMessageIndex] = useState(0)
|
||||
const windowWidth = useWindowWidth()
|
||||
|
||||
const onSelect = () => {
|
||||
onMessageSelected(messageIndex)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="row is-center">
|
||||
<div className="col-7-lg col-12 is-marginless">
|
||||
{messages.map((message, i) => (
|
||||
<RadioButtonContainer className="row is-center is-vertical-align" key={i} onClick={() => setMessageIndex(i)}>
|
||||
<input
|
||||
className="is-marginless"
|
||||
type="radio"
|
||||
name="message"
|
||||
value={i}
|
||||
checked={i === messageIndex}
|
||||
onChange={() => setMessageIndex(i)}
|
||||
/>
|
||||
<RadioButtonLabel htmlFor={i.toString()}>
|
||||
{windowWidth < 700 ? formatTxHashExtended(message.id) : message.id}
|
||||
</RadioButtonLabel>
|
||||
</RadioButtonContainer>
|
||||
))}
|
||||
</div>
|
||||
<div className="col-1-lg col-12 is-marginless">
|
||||
<Button className="button outline" onClick={onSelect}>
|
||||
Select
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
47
alm/src/components/NetworkTransactionSelector.tsx
Normal file
47
alm/src/components/NetworkTransactionSelector.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React, { useState } from 'react'
|
||||
import { Button } from './commons/Button'
|
||||
import { RadioButtonLabel, RadioButtonContainer } from './commons/RadioButton'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
|
||||
export const NetworkTransactionSelector = ({ onNetworkSelected }: { onNetworkSelected: (chainId: number) => void }) => {
|
||||
const { home, foreign } = useStateProvider()
|
||||
const [chainId, setChainId] = useState(home.chainId)
|
||||
|
||||
const networks = [home, foreign]
|
||||
|
||||
const onSelect = () => {
|
||||
onNetworkSelected(chainId)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>The transaction was found in both networks, please select one:</p>
|
||||
<div className="row is-center">
|
||||
<div className="col-3-lg col-12 is-marginless">
|
||||
{networks.map((network, i) => (
|
||||
<RadioButtonContainer
|
||||
className="row is-center is-vertical-align"
|
||||
key={i}
|
||||
onClick={() => setChainId(network.chainId)}
|
||||
>
|
||||
<input
|
||||
className="is-marginless"
|
||||
type="radio"
|
||||
name="message"
|
||||
value={network.chainId}
|
||||
checked={network.chainId === chainId}
|
||||
onChange={() => setChainId(network.chainId)}
|
||||
/>
|
||||
<RadioButtonLabel htmlFor={i.toString()}>{network.name}</RadioButtonLabel>
|
||||
</RadioButtonContainer>
|
||||
))}
|
||||
</div>
|
||||
<div className="col-3-lg col-12 is-marginless">
|
||||
<Button className="button outline" onClick={onSelect}>
|
||||
Select
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
122
alm/src/components/StatusContainer.tsx
Normal file
122
alm/src/components/StatusContainer.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { Link, useHistory, useParams } from 'react-router-dom'
|
||||
import { useTransactionStatus } from '../hooks/useTransactionStatus'
|
||||
import { formatTxHash, getExplorerTxUrl, getTransactionStatusDescription, validTxHash } from '../utils/networks'
|
||||
import { TRANSACTION_STATUS } from '../config/constants'
|
||||
import { MessageSelector } from './MessageSelector'
|
||||
import { Loading } from './commons/Loading'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
||||
import { ConfirmationsContainer } from './ConfirmationsContainer'
|
||||
import { LeftArrow } from './commons/LeftArrow'
|
||||
import styled from 'styled-components'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
|
||||
const BackButton = styled.button`
|
||||
color: var(--button-color);
|
||||
border-color: var(--font-color);
|
||||
margin-top: 10px;
|
||||
&:focus {
|
||||
outline: var(--button-color);
|
||||
}
|
||||
`
|
||||
|
||||
const BackLabel = styled.label`
|
||||
margin-left: 5px;
|
||||
cursor: pointer;
|
||||
`
|
||||
|
||||
export interface StatusContainerParam {
|
||||
onBackToMain: () => void
|
||||
setNetworkFromParams: (chainId: number) => void
|
||||
receiptParam: Maybe<TransactionReceipt>
|
||||
}
|
||||
|
||||
export const StatusContainer = ({ onBackToMain, setNetworkFromParams, receiptParam }: StatusContainerParam) => {
|
||||
const { home, foreign } = useStateProvider()
|
||||
const history = useHistory()
|
||||
const { chainId, txHash, messageIdParam } = useParams()
|
||||
const validChainId = chainId === home.chainId.toString() || chainId === foreign.chainId.toString()
|
||||
const validParameters = validChainId && validTxHash(txHash)
|
||||
|
||||
const { messages, receipt, status, description, timestamp, loading } = useTransactionStatus({
|
||||
txHash: validParameters ? txHash : '',
|
||||
chainId: validParameters ? parseInt(chainId) : 0,
|
||||
receiptParam
|
||||
})
|
||||
|
||||
const selectedMessageId = messageIdParam === undefined || messages[messageIdParam] === undefined ? -1 : messageIdParam
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (validChainId) {
|
||||
setNetworkFromParams(parseInt(chainId))
|
||||
}
|
||||
},
|
||||
[validChainId, chainId, setNetworkFromParams]
|
||||
)
|
||||
|
||||
if (!validParameters && home.chainId && foreign.chainId) {
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
Chain Id: {chainId} and/or Transaction Hash: {txHash} are not valid
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return <Loading />
|
||||
}
|
||||
|
||||
const onMessageSelected = (messageId: number) => {
|
||||
history.push(`/${chainId}/${txHash}/${messageId}`)
|
||||
}
|
||||
|
||||
const displayMessageSelector = status === TRANSACTION_STATUS.SUCCESS_MULTIPLE_MESSAGES && selectedMessageId === -1
|
||||
const multiMessageSelected = status === TRANSACTION_STATUS.SUCCESS_MULTIPLE_MESSAGES && selectedMessageId !== -1
|
||||
const displayReference = multiMessageSelected ? messages[selectedMessageId].id : txHash
|
||||
const formattedMessageId = formatTxHash(displayReference)
|
||||
|
||||
const displayedDescription = multiMessageSelected
|
||||
? getTransactionStatusDescription(TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE, timestamp)
|
||||
: description
|
||||
|
||||
const isHome = chainId === home.chainId.toString()
|
||||
const txExplorerLink = getExplorerTxUrl(txHash, isHome)
|
||||
const displayExplorerLink = status !== TRANSACTION_STATUS.NOT_FOUND
|
||||
|
||||
const displayConfirmations = status === TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE || multiMessageSelected
|
||||
const messageToConfirm =
|
||||
messages.length > 1 ? messages[selectedMessageId] : messages.length > 0 ? messages[0] : { id: '', data: '' }
|
||||
return (
|
||||
<div>
|
||||
{status && (
|
||||
<p>
|
||||
The request{' '}
|
||||
{displayExplorerLink && (
|
||||
<ExplorerTxLink href={txExplorerLink} target="blank">
|
||||
{formattedMessageId}
|
||||
</ExplorerTxLink>
|
||||
)}
|
||||
{!displayExplorerLink && <label>{formattedMessageId}</label>} {displayedDescription}
|
||||
</p>
|
||||
)}
|
||||
{displayMessageSelector && <MessageSelector messages={messages} onMessageSelected={onMessageSelected} />}
|
||||
{displayConfirmations && (
|
||||
<ConfirmationsContainer message={messageToConfirm} receipt={receipt} fromHome={isHome} timestamp={timestamp} />
|
||||
)}
|
||||
<div className="row is-center">
|
||||
<div className="col-9">
|
||||
<Link to="/" onClick={onBackToMain}>
|
||||
<BackButton className="button outline is-left">
|
||||
<LeftArrow />
|
||||
<BackLabel>Search another transaction</BackLabel>
|
||||
</BackButton>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
47
alm/src/components/TransactionSelector.tsx
Normal file
47
alm/src/components/TransactionSelector.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import React, { useEffect } from 'react'
|
||||
import { useTransactionFinder } from '../hooks/useTransactionFinder'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { TRANSACTION_STATUS } from '../config/constants'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { Loading } from './commons/Loading'
|
||||
import { NetworkTransactionSelector } from './NetworkTransactionSelector'
|
||||
|
||||
export const TransactionSelector = ({
|
||||
txHash,
|
||||
onSelected
|
||||
}: {
|
||||
txHash: string
|
||||
onSelected: (chainId: number, receipt: TransactionReceipt) => void
|
||||
}) => {
|
||||
const { home, foreign } = useStateProvider()
|
||||
const { receipt: homeReceipt, status: homeStatus } = useTransactionFinder({ txHash, web3: home.web3 })
|
||||
const { receipt: foreignReceipt, status: foreignStatus } = useTransactionFinder({ txHash, web3: foreign.web3 })
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!home.chainId || !foreign.chainId) return
|
||||
if (homeStatus === TRANSACTION_STATUS.FOUND && foreignStatus === TRANSACTION_STATUS.NOT_FOUND) {
|
||||
if (!homeReceipt) return
|
||||
onSelected(home.chainId, homeReceipt)
|
||||
} else if (foreignStatus === TRANSACTION_STATUS.FOUND && homeStatus === TRANSACTION_STATUS.NOT_FOUND) {
|
||||
if (!foreignReceipt) return
|
||||
onSelected(foreign.chainId, foreignReceipt)
|
||||
}
|
||||
},
|
||||
[homeReceipt, homeStatus, foreignReceipt, foreignStatus, home.chainId, foreign.chainId, onSelected]
|
||||
)
|
||||
|
||||
const onSelectedNetwork = (chainId: number) => {
|
||||
const chain = chainId === home.chainId ? home.chainId : foreign.chainId
|
||||
const receipt = chainId === home.chainId ? homeReceipt : foreignReceipt
|
||||
|
||||
if (!receipt) return
|
||||
onSelected(chain, receipt)
|
||||
}
|
||||
|
||||
if (foreignStatus === TRANSACTION_STATUS.FOUND && homeStatus === TRANSACTION_STATUS.FOUND) {
|
||||
return <NetworkTransactionSelector onNetworkSelected={onSelectedNetwork} />
|
||||
}
|
||||
|
||||
return <Loading />
|
||||
}
|
||||
91
alm/src/components/ValidatorsConfirmations.tsx
Normal file
91
alm/src/components/ValidatorsConfirmations.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import React from 'react'
|
||||
import { formatTimestamp, formatTxHash, getExplorerTxUrl } from '../utils/networks'
|
||||
import { useWindowWidth } from '@react-hook/window-size'
|
||||
import { VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import { SimpleLoading } from './commons/Loading'
|
||||
import styled from 'styled-components'
|
||||
import { ConfirmationParam } from '../hooks/useMessageConfirmations'
|
||||
import { GreyLabel, RedLabel, SuccessLabel } from './commons/Labels'
|
||||
import { ExplorerTxLink } from './commons/ExplorerTxLink'
|
||||
|
||||
const Thead = styled.thead`
|
||||
border-bottom: 2px solid #9e9e9e;
|
||||
`
|
||||
|
||||
const RequiredConfirmations = styled.label`
|
||||
font-size: 14px;
|
||||
`
|
||||
|
||||
export interface ValidatorsConfirmationsParams {
|
||||
confirmations: Array<ConfirmationParam>
|
||||
requiredSignatures: number
|
||||
validatorList: string[]
|
||||
}
|
||||
|
||||
export const ValidatorsConfirmations = ({
|
||||
confirmations,
|
||||
requiredSignatures,
|
||||
validatorList
|
||||
}: ValidatorsConfirmationsParams) => {
|
||||
const windowWidth = useWindowWidth()
|
||||
|
||||
const getValidatorStatusElement = (validatorStatus = '') => {
|
||||
switch (validatorStatus) {
|
||||
case VALIDATOR_CONFIRMATION_STATUS.SUCCESS:
|
||||
return <SuccessLabel>{validatorStatus}</SuccessLabel>
|
||||
case VALIDATOR_CONFIRMATION_STATUS.FAILED:
|
||||
return <RedLabel>{validatorStatus}</RedLabel>
|
||||
case VALIDATOR_CONFIRMATION_STATUS.PENDING:
|
||||
case VALIDATOR_CONFIRMATION_STATUS.WAITING:
|
||||
case VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED:
|
||||
return <GreyLabel>{validatorStatus}</GreyLabel>
|
||||
default:
|
||||
return <SimpleLoading />
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<table>
|
||||
<Thead>
|
||||
<tr>
|
||||
<th>Validator</th>
|
||||
<th className="text-center">Status</th>
|
||||
<th className="text-center">Age</th>
|
||||
</tr>
|
||||
</Thead>
|
||||
<tbody>
|
||||
{validatorList.map((validator, i) => {
|
||||
const filteredConfirmation = confirmations.filter(c => c.validator === validator)
|
||||
const confirmation = filteredConfirmation.length > 0 ? filteredConfirmation[0] : null
|
||||
const displayedStatus = confirmation && confirmation.status ? confirmation.status : ''
|
||||
const explorerLink = confirmation && confirmation.txHash ? getExplorerTxUrl(confirmation.txHash, true) : ''
|
||||
const elementIfNoTimestamp =
|
||||
displayedStatus !== VALIDATOR_CONFIRMATION_STATUS.WAITING &&
|
||||
displayedStatus !== VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED ? (
|
||||
<SimpleLoading />
|
||||
) : (
|
||||
''
|
||||
)
|
||||
return (
|
||||
<tr key={i}>
|
||||
<td>{windowWidth < 850 ? formatTxHash(validator) : validator}</td>
|
||||
<td className="text-center">{getValidatorStatusElement(displayedStatus)}</td>
|
||||
<td className="text-center">
|
||||
<ExplorerTxLink href={explorerLink} target="_blank">
|
||||
{confirmation && confirmation.timestamp > 0
|
||||
? formatTimestamp(confirmation.timestamp)
|
||||
: elementIfNoTimestamp}
|
||||
</ExplorerTxLink>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
<RequiredConfirmations>
|
||||
{requiredSignatures} of {validatorList.length} confirmations required
|
||||
</RequiredConfirmations>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
10
alm/src/components/commons/Button.tsx
Normal file
10
alm/src/components/commons/Button.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const Button = styled.button`
|
||||
height: 36px;
|
||||
color: var(--button-color);
|
||||
border-color: var(--button-color);
|
||||
&:focus {
|
||||
outline: var(--button-color);
|
||||
}
|
||||
`
|
||||
7
alm/src/components/commons/ExplorerTxLink.tsx
Normal file
7
alm/src/components/commons/ExplorerTxLink.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const ExplorerTxLink = styled.a`
|
||||
color: var(--link-color);
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
`
|
||||
22
alm/src/components/commons/Labels.tsx
Normal file
22
alm/src/components/commons/Labels.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const SuccessLabel = styled.label`
|
||||
color: var(--success-color);
|
||||
background-color: var(--success-bg-color);
|
||||
padding: 0.4rem 0.7rem;
|
||||
border-radius: 4px;
|
||||
`
|
||||
|
||||
export const GreyLabel = styled.label`
|
||||
color: var(--not-required-color);
|
||||
background-color: var(--not-required-bg-color);
|
||||
padding: 0.4rem 0.7rem;
|
||||
border-radius: 4px;
|
||||
`
|
||||
|
||||
export const RedLabel = styled.label`
|
||||
color: var(--failed-color);
|
||||
background-color: var(--failed-bg-color);
|
||||
padding: 0.4rem 0.7rem;
|
||||
border-radius: 4px;
|
||||
`
|
||||
20
alm/src/components/commons/LeftArrow.tsx
Normal file
20
alm/src/components/commons/LeftArrow.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import { useContext } from 'react'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
|
||||
export const LeftArrow = () => {
|
||||
const themeContext = useContext(ThemeContext)
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
id="mdi-arrow-left"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill={themeContext.buttonColor}
|
||||
>
|
||||
<path d="M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
165
alm/src/components/commons/Loading.tsx
Normal file
165
alm/src/components/commons/Loading.tsx
Normal file
@@ -0,0 +1,165 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { ThemeContext } from 'styled-components'
|
||||
|
||||
export interface LoadingParams {
|
||||
width?: string
|
||||
height?: string
|
||||
displayMessage?: boolean
|
||||
}
|
||||
|
||||
export const Loading = ({ width = '50px', height = '50px', displayMessage = true }: LoadingParams) => {
|
||||
const themeContext = useContext(ThemeContext)
|
||||
return (
|
||||
<div className="row is-center">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
style={{ background: 'none', display: 'block', shapeRendering: 'auto' }}
|
||||
width={width}
|
||||
height={height}
|
||||
viewBox="0 0 100 100"
|
||||
preserveAspectRatio="xMidYMid"
|
||||
>
|
||||
<g transform="rotate(0 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.9166666666666666s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(30 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.8333333333333334s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(60 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.75s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(90 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.6666666666666666s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(120 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.5833333333333334s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(150 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.5s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(180 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.4166666666666667s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(210 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.3333333333333333s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(240 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.25s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(270 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.16666666666666666s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(300 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate
|
||||
attributeName="opacity"
|
||||
values="1;0"
|
||||
keyTimes="0;1"
|
||||
dur="1s"
|
||||
begin="-0.08333333333333333s"
|
||||
repeatCount="indefinite"
|
||||
/>
|
||||
</rect>
|
||||
</g>
|
||||
<g transform="rotate(330 50 50)">
|
||||
<rect x="47" y="24" rx="3" ry="6" width="6" height="12" fill={themeContext.buttonColor}>
|
||||
<animate attributeName="opacity" values="1;0" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite" />
|
||||
</rect>
|
||||
</g>
|
||||
</svg>
|
||||
{displayMessage && <label style={{ color: themeContext.buttonColor }}>Loading...</label>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export const SimpleLoading = () => <Loading width="30px" height="30px" displayMessage={false} />
|
||||
9
alm/src/components/commons/RadioButton.tsx
Normal file
9
alm/src/components/commons/RadioButton.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import styled from 'styled-components'
|
||||
|
||||
export const RadioButtonLabel = styled.label`
|
||||
padding-left: 5px;
|
||||
`
|
||||
|
||||
export const RadioButtonContainer = styled.div`
|
||||
padding: 10px;
|
||||
`
|
||||
59
alm/src/config/constants.ts
Normal file
59
alm/src/config/constants.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
export const HOME_BRIDGE_ADDRESS: string = process.env.REACT_APP_COMMON_HOME_BRIDGE_ADDRESS || ''
|
||||
export const FOREIGN_BRIDGE_ADDRESS: string = process.env.REACT_APP_COMMON_FOREIGN_BRIDGE_ADDRESS || ''
|
||||
|
||||
export const HOME_RPC_URL: string = process.env.REACT_APP_COMMON_HOME_RPC_URL || ''
|
||||
export const FOREIGN_RPC_URL: string = process.env.REACT_APP_COMMON_FOREIGN_RPC_URL || ''
|
||||
|
||||
export const HOME_NETWORK_NAME: string = process.env.REACT_APP_ALM_HOME_NETWORK_NAME || ''
|
||||
export const FOREIGN_NETWORK_NAME: string = process.env.REACT_APP_ALM_FOREIGN_NETWORK_NAME || ''
|
||||
|
||||
export const HOME_EXPLORER_TX_TEMPLATE: string = process.env.REACT_APP_ALM_HOME_EXPLORER_TX_TEMPLATE || ''
|
||||
export const FOREIGN_EXPLORER_TX_TEMPLATE: string = process.env.REACT_APP_ALM_FOREIGN_EXPLORER_TX_TEMPLATE || ''
|
||||
|
||||
export const HOME_EXPLORER_API: string = process.env.REACT_APP_ALM_HOME_EXPLORER_API || ''
|
||||
export const FOREIGN_EXPLORER_API: string = process.env.REACT_APP_ALM_FOREIGN_EXPLORER_API || ''
|
||||
|
||||
export const HOME_RPC_POLLING_INTERVAL: number = 5000
|
||||
export const FOREIGN_RPC_POLLING_INTERVAL: number = 5000
|
||||
export const BLOCK_RANGE: number = 50
|
||||
export const ONE_DAY_TIMESTAMP: number = 86400
|
||||
export const THREE_DAYS_TIMESTAMP: number = 259200
|
||||
|
||||
export const EXECUTE_AFFIRMATION_HASH = 'e7a2c01f'
|
||||
export const SUBMIT_SIGNATURE_HASH = '630cea8e'
|
||||
export const EXECUTE_SIGNATURES_HASH = '3f7658fd'
|
||||
|
||||
export const CACHE_KEY_SUCCESS = 'success-confirmation-validator-'
|
||||
export const CACHE_KEY_FAILED = 'failed-confirmation-validator-'
|
||||
export const CACHE_KEY_EXECUTION_FAILED = 'failed-execution-validator-'
|
||||
|
||||
export const TRANSACTION_STATUS = {
|
||||
SUCCESS_MULTIPLE_MESSAGES: 'SUCCESS_MULTIPLE_MESSAGES',
|
||||
SUCCESS_ONE_MESSAGE: 'SUCCESS_ONE_MESSAGE',
|
||||
SUCCESS_NO_MESSAGES: 'SUCCESS_NO_MESSAGES',
|
||||
FAILED: 'FAILED',
|
||||
FOUND: 'FOUND',
|
||||
NOT_FOUND: 'NOT_FOUND',
|
||||
UNDEFINED: 'UNDEFINED'
|
||||
}
|
||||
|
||||
export const CONFIRMATIONS_STATUS = {
|
||||
SUCCESS: 'SUCCESS',
|
||||
SUCCESS_MESSAGE_FAILED: 'SUCCESS_MESSAGE_FAILED',
|
||||
EXECUTION_FAILED: 'EXECUTION_FAILED',
|
||||
EXECUTION_PENDING: 'EXECUTION_PENDING',
|
||||
EXECUTION_WAITING: 'EXECUTION_WAITING',
|
||||
FAILED: 'FAILED',
|
||||
PENDING: 'PENDING',
|
||||
WAITING: 'WAITING',
|
||||
UNDEFINED: 'UNDEFINED'
|
||||
}
|
||||
|
||||
export const VALIDATOR_CONFIRMATION_STATUS = {
|
||||
SUCCESS: 'Success',
|
||||
FAILED: 'Failed',
|
||||
PENDING: 'Pending',
|
||||
WAITING: 'Waiting',
|
||||
NOT_REQUIRED: 'Not required',
|
||||
UNDEFINED: 'UNDEFINED'
|
||||
}
|
||||
36
alm/src/config/descriptions.ts
Normal file
36
alm/src/config/descriptions.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
// %t will be replaced by the time -> x minutes/hours/days ago
|
||||
export const TRANSACTION_STATUS_DESCRIPTION: { [key: string]: string } = {
|
||||
SUCCESS_MULTIPLE_MESSAGES: 'was initiated %t and contains several bridge messages. Specify one of them:',
|
||||
SUCCESS_ONE_MESSAGE: 'was initiated %t',
|
||||
SUCCESS_NO_MESSAGES: 'execution succeeded %t but it does not contain any bridge messages',
|
||||
FAILED: 'failed %t',
|
||||
NOT_FOUND: 'was not found'
|
||||
}
|
||||
|
||||
export const CONFIRMATIONS_STATUS_LABEL: { [key: string]: string } = {
|
||||
SUCCESS: 'Success',
|
||||
SUCCESS_MESSAGE_FAILED: 'Success',
|
||||
EXECUTION_FAILED: 'Execution failed',
|
||||
EXECUTION_PENDING: 'Execution pending',
|
||||
EXECUTION_WAITING: 'Execution waiting',
|
||||
FAILED: 'Failed',
|
||||
PENDING: 'Pending',
|
||||
WAITING: 'Waiting'
|
||||
}
|
||||
|
||||
// %homeChain will be replaced by the home network name
|
||||
// %foreignChain will be replaced by the foreign network name
|
||||
export const CONFIRMATIONS_STATUS_DESCRIPTION: { [key: string]: string } = {
|
||||
SUCCESS: '',
|
||||
SUCCESS_MESSAGE_FAILED:
|
||||
'Signatures have been collected in the %homeChain and they were successfully sent to the %foreignChain but the contained message execution failed.',
|
||||
EXECUTION_FAILED:
|
||||
'Signatures have been collected in the %homeChain and they were sent to the %foreignChain but the transaction with signatures failed',
|
||||
EXECUTION_PENDING:
|
||||
'Signatures have been collected in the %homeChain and they were sent to the %foreignChain but the transaction is in the pending state (transactions congestion or low gas price)',
|
||||
EXECUTION_WAITING: 'Execution waiting',
|
||||
FAILED:
|
||||
'Some validators sent improper transactions as so they were failed, collected confirmations are not enough to execute the relay request',
|
||||
PENDING: 'Some confirmations are in pending state',
|
||||
WAITING: 'Validators are waiting for the chain finalization'
|
||||
}
|
||||
1
alm/src/global.d.ts
vendored
Normal file
1
alm/src/global.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
declare type Maybe<T> = T | null
|
||||
38
alm/src/hooks/useBlockConfirmations.ts
Normal file
38
alm/src/hooks/useBlockConfirmations.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
import { getRequiredBlockConfirmations } from '../utils/contract'
|
||||
|
||||
export interface UseBlockConfirmationsParams {
|
||||
fromHome: boolean
|
||||
receipt: Maybe<TransactionReceipt>
|
||||
}
|
||||
|
||||
export const useBlockConfirmations = ({ receipt, fromHome }: UseBlockConfirmationsParams) => {
|
||||
const [blockConfirmations, setBlockConfirmations] = useState(0)
|
||||
|
||||
const { home, foreign } = useStateProvider()
|
||||
|
||||
const callRequireBlockConfirmations = async (
|
||||
contract: Contract,
|
||||
receipt: TransactionReceipt,
|
||||
setResult: Function
|
||||
) => {
|
||||
const result = await getRequiredBlockConfirmations(contract, receipt.blockNumber)
|
||||
setResult(result)
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
|
||||
if (!bridgeContract || !receipt) return
|
||||
callRequireBlockConfirmations(bridgeContract, receipt, setBlockConfirmations)
|
||||
},
|
||||
[home.bridgeContract, foreign.bridgeContract, receipt, fromHome]
|
||||
)
|
||||
|
||||
return {
|
||||
blockConfirmations
|
||||
}
|
||||
}
|
||||
38
alm/src/hooks/useBridgeContracts.ts
Normal file
38
alm/src/hooks/useBridgeContracts.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { HOME_AMB_ABI, FOREIGN_AMB_ABI } from '../abis'
|
||||
import { FOREIGN_BRIDGE_ADDRESS, HOME_BRIDGE_ADDRESS } from '../config/constants'
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
import Web3 from 'web3'
|
||||
|
||||
export interface useBridgeContractsParams {
|
||||
homeWeb3: Web3
|
||||
foreignWeb3: Web3
|
||||
}
|
||||
|
||||
export const useBridgeContracts = ({ homeWeb3, foreignWeb3 }: useBridgeContractsParams) => {
|
||||
const [homeBridge, setHomeBridge] = useState<Maybe<Contract>>(null)
|
||||
const [foreignBridge, setForeignBridge] = useState<Maybe<Contract>>(null)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!homeWeb3) return
|
||||
const homeContract = new homeWeb3.eth.Contract(HOME_AMB_ABI, HOME_BRIDGE_ADDRESS)
|
||||
setHomeBridge(homeContract)
|
||||
},
|
||||
[homeWeb3]
|
||||
)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!foreignWeb3) return
|
||||
const foreignContract = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, FOREIGN_BRIDGE_ADDRESS)
|
||||
setForeignBridge(foreignContract)
|
||||
},
|
||||
[foreignWeb3]
|
||||
)
|
||||
|
||||
return {
|
||||
homeBridge,
|
||||
foreignBridge
|
||||
}
|
||||
}
|
||||
357
alm/src/hooks/useMessageConfirmations.ts
Normal file
357
alm/src/hooks/useMessageConfirmations.ts
Normal file
@@ -0,0 +1,357 @@
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { MessageObject } from '../utils/web3'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { EventData } from 'web3-eth-contract'
|
||||
import { getAffirmationsSigned, getMessagesSigned } from '../utils/contract'
|
||||
import {
|
||||
BLOCK_RANGE,
|
||||
CONFIRMATIONS_STATUS,
|
||||
FOREIGN_RPC_POLLING_INTERVAL,
|
||||
HOME_RPC_POLLING_INTERVAL,
|
||||
VALIDATOR_CONFIRMATION_STATUS
|
||||
} from '../config/constants'
|
||||
import { homeBlockNumberProvider, foreignBlockNumberProvider } from '../services/BlockNumberProvider'
|
||||
import { checkSignaturesWaitingForBLocks } from '../utils/signatureWaitingForBlocks'
|
||||
import { getCollectedSignaturesEvent } from '../utils/getCollectedSignaturesEvent'
|
||||
import { checkWaitingBlocksForExecution } from '../utils/executionWaitingForBlocks'
|
||||
import { getConfirmationsForTx } from '../utils/getConfirmationsForTx'
|
||||
import { getFinalizationEvent } from '../utils/getFinalizationEvent'
|
||||
import {
|
||||
getValidatorFailedTransactionsForMessage,
|
||||
getExecutionFailedTransactionForMessage,
|
||||
getValidatorPendingTransactionsForMessage,
|
||||
getExecutionPendingTransactionsForMessage,
|
||||
getValidatorSuccessTransactionsForMessage
|
||||
} from '../utils/explorer'
|
||||
|
||||
export interface useMessageConfirmationsParams {
|
||||
message: MessageObject
|
||||
receipt: Maybe<TransactionReceipt>
|
||||
fromHome: boolean
|
||||
timestamp: number
|
||||
requiredSignatures: number
|
||||
validatorList: string[]
|
||||
blockConfirmations: number
|
||||
}
|
||||
|
||||
export interface BasicConfirmationParam {
|
||||
validator: string
|
||||
status: string
|
||||
}
|
||||
|
||||
export interface ConfirmationParam extends BasicConfirmationParam {
|
||||
txHash: string
|
||||
timestamp: number
|
||||
}
|
||||
|
||||
export interface ExecutionData {
|
||||
status: string
|
||||
validator: string
|
||||
txHash: string
|
||||
timestamp: number
|
||||
executionResult: boolean
|
||||
}
|
||||
|
||||
export const useMessageConfirmations = ({
|
||||
message,
|
||||
receipt,
|
||||
fromHome,
|
||||
timestamp,
|
||||
requiredSignatures,
|
||||
validatorList,
|
||||
blockConfirmations
|
||||
}: useMessageConfirmationsParams) => {
|
||||
const { home, foreign } = useStateProvider()
|
||||
const [confirmations, setConfirmations] = useState<Array<ConfirmationParam>>([])
|
||||
const [status, setStatus] = useState(CONFIRMATIONS_STATUS.UNDEFINED)
|
||||
const [waitingBlocks, setWaitingBlocks] = useState(false)
|
||||
const [waitingBlocksResolved, setWaitingBlocksResolved] = useState(false)
|
||||
const [signatureCollected, setSignatureCollected] = useState(false)
|
||||
const [collectedSignaturesEvent, setCollectedSignaturesEvent] = useState<Maybe<EventData>>(null)
|
||||
const [executionData, setExecutionData] = useState<ExecutionData>({
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||
validator: '',
|
||||
txHash: '',
|
||||
timestamp: 0,
|
||||
executionResult: false
|
||||
})
|
||||
const [waitingBlocksForExecution, setWaitingBlocksForExecution] = useState(false)
|
||||
const [waitingBlocksForExecutionResolved, setWaitingBlocksForExecutionResolved] = useState(false)
|
||||
const [failedConfirmations, setFailedConfirmations] = useState(false)
|
||||
const [failedExecution, setFailedExecution] = useState(false)
|
||||
const [pendingConfirmations, setPendingConfirmations] = useState(false)
|
||||
const [pendingExecution, setPendingExecution] = useState(false)
|
||||
|
||||
// Check if the validators are waiting for block confirmations to verify the message
|
||||
useEffect(
|
||||
() => {
|
||||
if (!receipt || !blockConfirmations) return
|
||||
|
||||
const subscriptions: Array<number> = []
|
||||
|
||||
const unsubscribe = () => {
|
||||
subscriptions.forEach(s => {
|
||||
clearTimeout(s)
|
||||
})
|
||||
}
|
||||
|
||||
const blockProvider = fromHome ? homeBlockNumberProvider : foreignBlockNumberProvider
|
||||
const interval = fromHome ? HOME_RPC_POLLING_INTERVAL : FOREIGN_RPC_POLLING_INTERVAL
|
||||
const web3 = fromHome ? home.web3 : foreign.web3
|
||||
blockProvider.start(web3)
|
||||
|
||||
const targetBlock = receipt.blockNumber + blockConfirmations
|
||||
|
||||
checkSignaturesWaitingForBLocks(
|
||||
targetBlock,
|
||||
setWaitingBlocks,
|
||||
setWaitingBlocksResolved,
|
||||
validatorList,
|
||||
setConfirmations,
|
||||
blockProvider,
|
||||
interval,
|
||||
subscriptions
|
||||
)
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
blockProvider.stop()
|
||||
}
|
||||
},
|
||||
[blockConfirmations, foreign.web3, fromHome, validatorList, home.web3, receipt]
|
||||
)
|
||||
|
||||
// The collected signature event is only fetched once the signatures are collected on tx from home to foreign, to calculate if
|
||||
// the execution tx on the foreign network is waiting for block confirmations
|
||||
// This is executed if the message is in Home to Foreign direction only
|
||||
useEffect(
|
||||
() => {
|
||||
if (!fromHome || !receipt || !home.web3 || !signatureCollected) return
|
||||
|
||||
const subscriptions: Array<number> = []
|
||||
|
||||
const unsubscribe = () => {
|
||||
subscriptions.forEach(s => {
|
||||
clearTimeout(s)
|
||||
})
|
||||
}
|
||||
|
||||
homeBlockNumberProvider.start(home.web3)
|
||||
|
||||
const fromBlock = receipt.blockNumber
|
||||
const toBlock = fromBlock + BLOCK_RANGE
|
||||
const messageHash = home.web3.utils.soliditySha3Raw(message.data)
|
||||
|
||||
getCollectedSignaturesEvent(
|
||||
home.web3,
|
||||
home.bridgeContract,
|
||||
fromBlock,
|
||||
toBlock,
|
||||
messageHash,
|
||||
setCollectedSignaturesEvent,
|
||||
subscriptions
|
||||
)
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
homeBlockNumberProvider.stop()
|
||||
}
|
||||
},
|
||||
[fromHome, home.bridgeContract, home.web3, message.data, receipt, signatureCollected]
|
||||
)
|
||||
|
||||
// Check if the responsible validator is waiting for block confirmations to execute the message on foreign network
|
||||
// This is executed if the message is in Home to Foreign direction only
|
||||
useEffect(
|
||||
() => {
|
||||
if (!fromHome || !home.web3 || !receipt || !collectedSignaturesEvent || !blockConfirmations) return
|
||||
|
||||
const subscriptions: Array<number> = []
|
||||
|
||||
const unsubscribe = () => {
|
||||
subscriptions.forEach(s => {
|
||||
clearTimeout(s)
|
||||
})
|
||||
}
|
||||
|
||||
homeBlockNumberProvider.start(home.web3)
|
||||
const targetBlock = collectedSignaturesEvent.blockNumber + blockConfirmations
|
||||
|
||||
checkWaitingBlocksForExecution(
|
||||
homeBlockNumberProvider,
|
||||
HOME_RPC_POLLING_INTERVAL,
|
||||
targetBlock,
|
||||
collectedSignaturesEvent,
|
||||
setWaitingBlocksForExecution,
|
||||
setWaitingBlocksForExecutionResolved,
|
||||
setExecutionData,
|
||||
subscriptions
|
||||
)
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
homeBlockNumberProvider.stop()
|
||||
}
|
||||
},
|
||||
[collectedSignaturesEvent, fromHome, blockConfirmations, home.web3, receipt]
|
||||
)
|
||||
|
||||
// Checks if validators verified the message
|
||||
// To avoid making extra requests, this is only executed when validators finished waiting for blocks confirmations
|
||||
useEffect(
|
||||
() => {
|
||||
if (!waitingBlocksResolved || !timestamp || !requiredSignatures) return
|
||||
|
||||
const subscriptions: Array<number> = []
|
||||
|
||||
const unsubscribe = () => {
|
||||
subscriptions.forEach(s => {
|
||||
clearTimeout(s)
|
||||
})
|
||||
}
|
||||
|
||||
const confirmationContractMethod = fromHome ? getMessagesSigned : getAffirmationsSigned
|
||||
|
||||
getConfirmationsForTx(
|
||||
message.data,
|
||||
home.web3,
|
||||
validatorList,
|
||||
home.bridgeContract,
|
||||
confirmationContractMethod,
|
||||
setConfirmations,
|
||||
requiredSignatures,
|
||||
setSignatureCollected,
|
||||
waitingBlocksResolved,
|
||||
subscriptions,
|
||||
timestamp,
|
||||
getValidatorFailedTransactionsForMessage,
|
||||
setFailedConfirmations,
|
||||
getValidatorPendingTransactionsForMessage,
|
||||
setPendingConfirmations,
|
||||
getValidatorSuccessTransactionsForMessage
|
||||
)
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
}
|
||||
},
|
||||
[
|
||||
fromHome,
|
||||
message.data,
|
||||
home.web3,
|
||||
validatorList,
|
||||
home.bridgeContract,
|
||||
requiredSignatures,
|
||||
waitingBlocksResolved,
|
||||
timestamp
|
||||
]
|
||||
)
|
||||
|
||||
// Gets finalization event to display the information about the execution of the message
|
||||
// In a message from Home to Foreign it will be executed after finishing waiting for block confirmations for the execution transaction on Foreign
|
||||
// In a message from Foreign to Home it will be executed after finishing waiting for block confirmations of the message request
|
||||
useEffect(
|
||||
() => {
|
||||
if ((fromHome && !waitingBlocksForExecutionResolved) || (!fromHome && !waitingBlocksResolved)) return
|
||||
|
||||
const subscriptions: Array<number> = []
|
||||
|
||||
const unsubscribe = () => {
|
||||
subscriptions.forEach(s => {
|
||||
clearTimeout(s)
|
||||
})
|
||||
}
|
||||
|
||||
const contractEvent = fromHome ? 'RelayedMessage' : 'AffirmationCompleted'
|
||||
const bridgeContract = fromHome ? foreign.bridgeContract : home.bridgeContract
|
||||
const providedWeb3 = fromHome ? foreign.web3 : home.web3
|
||||
const interval = fromHome ? FOREIGN_RPC_POLLING_INTERVAL : HOME_RPC_POLLING_INTERVAL
|
||||
|
||||
getFinalizationEvent(
|
||||
bridgeContract,
|
||||
contractEvent,
|
||||
providedWeb3,
|
||||
setExecutionData,
|
||||
waitingBlocksResolved,
|
||||
message,
|
||||
interval,
|
||||
subscriptions,
|
||||
timestamp,
|
||||
collectedSignaturesEvent,
|
||||
getExecutionFailedTransactionForMessage,
|
||||
setFailedExecution,
|
||||
getExecutionPendingTransactionsForMessage,
|
||||
setPendingExecution
|
||||
)
|
||||
|
||||
return () => {
|
||||
unsubscribe()
|
||||
}
|
||||
},
|
||||
[
|
||||
fromHome,
|
||||
foreign.bridgeContract,
|
||||
home.bridgeContract,
|
||||
message,
|
||||
foreign.web3,
|
||||
home.web3,
|
||||
waitingBlocksResolved,
|
||||
waitingBlocksForExecutionResolved,
|
||||
timestamp,
|
||||
collectedSignaturesEvent
|
||||
]
|
||||
)
|
||||
|
||||
// Sets the message status based in the collected information
|
||||
useEffect(
|
||||
() => {
|
||||
if (executionData.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS) {
|
||||
const newStatus = executionData.executionResult
|
||||
? CONFIRMATIONS_STATUS.SUCCESS
|
||||
: CONFIRMATIONS_STATUS.SUCCESS_MESSAGE_FAILED
|
||||
setStatus(newStatus)
|
||||
} else if (signatureCollected) {
|
||||
if (fromHome) {
|
||||
if (waitingBlocksForExecution) {
|
||||
setStatus(CONFIRMATIONS_STATUS.EXECUTION_WAITING)
|
||||
} else if (failedExecution) {
|
||||
setStatus(CONFIRMATIONS_STATUS.EXECUTION_FAILED)
|
||||
} else if (pendingExecution) {
|
||||
setStatus(CONFIRMATIONS_STATUS.EXECUTION_PENDING)
|
||||
} else {
|
||||
setStatus(CONFIRMATIONS_STATUS.UNDEFINED)
|
||||
}
|
||||
} else {
|
||||
setStatus(CONFIRMATIONS_STATUS.UNDEFINED)
|
||||
}
|
||||
} else if (waitingBlocks) {
|
||||
setStatus(CONFIRMATIONS_STATUS.WAITING)
|
||||
} else if (failedConfirmations) {
|
||||
setStatus(CONFIRMATIONS_STATUS.FAILED)
|
||||
} else if (pendingConfirmations) {
|
||||
setStatus(CONFIRMATIONS_STATUS.PENDING)
|
||||
} else {
|
||||
setStatus(CONFIRMATIONS_STATUS.UNDEFINED)
|
||||
}
|
||||
},
|
||||
[
|
||||
executionData,
|
||||
fromHome,
|
||||
signatureCollected,
|
||||
waitingBlocks,
|
||||
waitingBlocksForExecution,
|
||||
failedConfirmations,
|
||||
failedExecution,
|
||||
pendingConfirmations,
|
||||
pendingExecution
|
||||
]
|
||||
)
|
||||
|
||||
return {
|
||||
confirmations,
|
||||
status,
|
||||
signatureCollected,
|
||||
executionData
|
||||
}
|
||||
}
|
||||
27
alm/src/hooks/useNetwork.ts
Normal file
27
alm/src/hooks/useNetwork.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { getWeb3 } from '../utils/web3'
|
||||
|
||||
export const useNetwork = (url: string) => {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [chainId, setChainId] = useState(0)
|
||||
const web3 = getWeb3(url)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
setLoading(true)
|
||||
const getChainId = async () => {
|
||||
const id = await web3.eth.getChainId()
|
||||
setChainId(id)
|
||||
setLoading(false)
|
||||
}
|
||||
getChainId()
|
||||
},
|
||||
[web3.eth]
|
||||
)
|
||||
|
||||
return {
|
||||
web3,
|
||||
chainId,
|
||||
loading
|
||||
}
|
||||
}
|
||||
56
alm/src/hooks/useTransactionFinder.ts
Normal file
56
alm/src/hooks/useTransactionFinder.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { HOME_RPC_POLLING_INTERVAL, TRANSACTION_STATUS } from '../config/constants'
|
||||
import Web3 from 'web3'
|
||||
|
||||
export const useTransactionFinder = ({ txHash, web3 }: { txHash: string; web3: Maybe<Web3> }) => {
|
||||
const [status, setStatus] = useState(TRANSACTION_STATUS.UNDEFINED)
|
||||
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!txHash || !web3) return
|
||||
|
||||
const subscriptions: number[] = []
|
||||
|
||||
const unsubscribe = () => {
|
||||
subscriptions.forEach(s => {
|
||||
clearTimeout(s)
|
||||
})
|
||||
}
|
||||
|
||||
const getReceipt = async (
|
||||
web3: Web3,
|
||||
txHash: string,
|
||||
setReceipt: Function,
|
||||
setStatus: Function,
|
||||
subscriptions: number[]
|
||||
) => {
|
||||
const txReceipt = await web3.eth.getTransactionReceipt(txHash)
|
||||
setReceipt(txReceipt)
|
||||
|
||||
if (!txReceipt) {
|
||||
setStatus(TRANSACTION_STATUS.NOT_FOUND)
|
||||
const timeoutId = setTimeout(
|
||||
() => getReceipt(web3, txHash, setReceipt, setStatus, subscriptions),
|
||||
HOME_RPC_POLLING_INTERVAL
|
||||
)
|
||||
subscriptions.push(timeoutId)
|
||||
} else {
|
||||
setStatus(TRANSACTION_STATUS.FOUND)
|
||||
}
|
||||
}
|
||||
|
||||
getReceipt(web3, txHash, setReceipt, setStatus, subscriptions)
|
||||
return () => {
|
||||
unsubscribe()
|
||||
}
|
||||
},
|
||||
[txHash, web3]
|
||||
)
|
||||
|
||||
return {
|
||||
status,
|
||||
receipt
|
||||
}
|
||||
}
|
||||
131
alm/src/hooks/useTransactionStatus.ts
Normal file
131
alm/src/hooks/useTransactionStatus.ts
Normal file
@@ -0,0 +1,131 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { HOME_RPC_POLLING_INTERVAL, TRANSACTION_STATUS } from '../config/constants'
|
||||
import { getTransactionStatusDescription } from '../utils/networks'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { getHomeMessagesFromReceipt, getForeignMessagesFromReceipt, MessageObject, getBlock } from '../utils/web3'
|
||||
import useInterval from '@use-it/interval'
|
||||
|
||||
export const useTransactionStatus = ({
|
||||
txHash,
|
||||
chainId,
|
||||
receiptParam
|
||||
}: {
|
||||
txHash: string
|
||||
chainId: number
|
||||
receiptParam: Maybe<TransactionReceipt>
|
||||
}) => {
|
||||
const { home, foreign } = useStateProvider()
|
||||
const [messages, setMessages] = useState<Array<MessageObject>>([])
|
||||
const [status, setStatus] = useState('')
|
||||
const [description, setDescription] = useState('')
|
||||
const [receipt, setReceipt] = useState<Maybe<TransactionReceipt>>(null)
|
||||
const [timestamp, setTimestamp] = useState(0)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
// Update description so the time displayed is accurate
|
||||
useInterval(() => {
|
||||
if (!status || !timestamp || !description) return
|
||||
setDescription(getTransactionStatusDescription(status, timestamp))
|
||||
}, 30000)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const subscriptions: Array<number> = []
|
||||
|
||||
const unsubscribe = () => {
|
||||
subscriptions.forEach(s => {
|
||||
clearTimeout(s)
|
||||
})
|
||||
}
|
||||
|
||||
const getReceipt = async () => {
|
||||
if (!chainId || !txHash || !home.chainId || !foreign.chainId || !home.web3 || !foreign.web3) return
|
||||
setLoading(true)
|
||||
const isHome = chainId === home.chainId
|
||||
const web3 = isHome ? home.web3 : foreign.web3
|
||||
|
||||
let txReceipt
|
||||
|
||||
if (receiptParam) {
|
||||
txReceipt = receiptParam
|
||||
} else {
|
||||
txReceipt = await web3.eth.getTransactionReceipt(txHash)
|
||||
}
|
||||
|
||||
setReceipt(txReceipt)
|
||||
|
||||
if (!txReceipt) {
|
||||
setStatus(TRANSACTION_STATUS.NOT_FOUND)
|
||||
setDescription(getTransactionStatusDescription(TRANSACTION_STATUS.NOT_FOUND))
|
||||
setMessages([{ id: txHash, data: '' }])
|
||||
const timeoutId = setTimeout(() => getReceipt(), HOME_RPC_POLLING_INTERVAL)
|
||||
subscriptions.push(timeoutId)
|
||||
} else {
|
||||
const blockNumber = txReceipt.blockNumber
|
||||
const block = await getBlock(web3, blockNumber)
|
||||
const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp
|
||||
setTimestamp(blockTimestamp)
|
||||
|
||||
if (txReceipt.status) {
|
||||
let bridgeMessages: Array<MessageObject>
|
||||
if (isHome) {
|
||||
bridgeMessages = getHomeMessagesFromReceipt(txReceipt, home.web3, home.bridgeAddress)
|
||||
} else {
|
||||
bridgeMessages = getForeignMessagesFromReceipt(txReceipt, foreign.web3, foreign.bridgeAddress)
|
||||
}
|
||||
|
||||
if (bridgeMessages.length === 0) {
|
||||
setMessages([{ id: txHash, data: '' }])
|
||||
setStatus(TRANSACTION_STATUS.SUCCESS_NO_MESSAGES)
|
||||
setDescription(getTransactionStatusDescription(TRANSACTION_STATUS.SUCCESS_NO_MESSAGES, blockTimestamp))
|
||||
} else if (bridgeMessages.length === 1) {
|
||||
setMessages(bridgeMessages)
|
||||
setStatus(TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE)
|
||||
setDescription(getTransactionStatusDescription(TRANSACTION_STATUS.SUCCESS_ONE_MESSAGE, blockTimestamp))
|
||||
} else {
|
||||
setMessages(bridgeMessages)
|
||||
setStatus(TRANSACTION_STATUS.SUCCESS_MULTIPLE_MESSAGES)
|
||||
setDescription(
|
||||
getTransactionStatusDescription(TRANSACTION_STATUS.SUCCESS_MULTIPLE_MESSAGES, blockTimestamp)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setStatus(TRANSACTION_STATUS.FAILED)
|
||||
setDescription(getTransactionStatusDescription(TRANSACTION_STATUS.FAILED, blockTimestamp))
|
||||
}
|
||||
}
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
// unsubscribe from previous txHash
|
||||
unsubscribe()
|
||||
|
||||
getReceipt()
|
||||
return () => {
|
||||
// unsubscribe when unmount component
|
||||
unsubscribe()
|
||||
}
|
||||
},
|
||||
[
|
||||
txHash,
|
||||
chainId,
|
||||
home.chainId,
|
||||
foreign.chainId,
|
||||
home.web3,
|
||||
foreign.web3,
|
||||
home.bridgeAddress,
|
||||
foreign.bridgeAddress,
|
||||
receiptParam
|
||||
]
|
||||
)
|
||||
|
||||
return {
|
||||
messages,
|
||||
status,
|
||||
description,
|
||||
receipt,
|
||||
timestamp,
|
||||
loading
|
||||
}
|
||||
}
|
||||
68
alm/src/hooks/useValidatorContract.ts
Normal file
68
alm/src/hooks/useValidatorContract.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
import Web3 from 'web3'
|
||||
import { getRequiredSignatures, getValidatorAddress, getValidatorList } from '../utils/contract'
|
||||
import { BRIDGE_VALIDATORS_ABI } from '../abis'
|
||||
import { useStateProvider } from '../state/StateProvider'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
|
||||
export interface useValidatorContractParams {
|
||||
fromHome: boolean
|
||||
receipt: Maybe<TransactionReceipt>
|
||||
}
|
||||
|
||||
export const useValidatorContract = ({ receipt, fromHome }: useValidatorContractParams) => {
|
||||
const [validatorContract, setValidatorContract] = useState<Maybe<Contract>>(null)
|
||||
const [requiredSignatures, setRequiredSignatures] = useState(0)
|
||||
const [validatorList, setValidatorList] = useState([])
|
||||
|
||||
const { home, foreign } = useStateProvider()
|
||||
|
||||
const callValidatorContract = async (bridgeContract: Maybe<Contract>, web3: Web3, setValidatorContract: Function) => {
|
||||
if (!web3 || !bridgeContract) return
|
||||
const address = await getValidatorAddress(bridgeContract)
|
||||
const contract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, address)
|
||||
setValidatorContract(contract)
|
||||
}
|
||||
|
||||
const callRequiredSignatures = async (
|
||||
contract: Maybe<Contract>,
|
||||
receipt: TransactionReceipt,
|
||||
setResult: Function
|
||||
) => {
|
||||
if (!contract) return
|
||||
const result = await getRequiredSignatures(contract, receipt.blockNumber)
|
||||
setResult(result)
|
||||
}
|
||||
|
||||
const callValidatorList = async (contract: Maybe<Contract>, receipt: TransactionReceipt, setResult: Function) => {
|
||||
if (!contract) return
|
||||
const result = await getValidatorList(contract, receipt.blockNumber)
|
||||
setResult(result)
|
||||
}
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
const web3 = fromHome ? home.web3 : foreign.web3
|
||||
const bridgeContract = fromHome ? home.bridgeContract : foreign.bridgeContract
|
||||
|
||||
if (!web3 || !bridgeContract) return
|
||||
callValidatorContract(bridgeContract, web3, setValidatorContract)
|
||||
},
|
||||
[home.web3, foreign.web3, home.bridgeContract, foreign.bridgeContract, fromHome]
|
||||
)
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
if (!receipt) return
|
||||
callRequiredSignatures(validatorContract, receipt, setRequiredSignatures)
|
||||
callValidatorList(validatorContract, receipt, setValidatorList)
|
||||
},
|
||||
[validatorContract, receipt]
|
||||
)
|
||||
|
||||
return {
|
||||
requiredSignatures,
|
||||
validatorList
|
||||
}
|
||||
}
|
||||
16
alm/src/index.tsx
Normal file
16
alm/src/index.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { ThemeProvider } from 'styled-components'
|
||||
import { GlobalStyle } from './themes/GlobalStyle'
|
||||
import App from './App'
|
||||
import Light from './themes/Light'
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<ThemeProvider theme={Light}>
|
||||
<GlobalStyle />
|
||||
<App />
|
||||
</ThemeProvider>
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
1
alm/src/react-app-env.d.ts
vendored
Normal file
1
alm/src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
64
alm/src/services/BlockNumberProvider.ts
Normal file
64
alm/src/services/BlockNumberProvider.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import Web3 from 'web3'
|
||||
import differenceInMilliseconds from 'date-fns/differenceInMilliseconds'
|
||||
import { HOME_RPC_POLLING_INTERVAL } from '../config/constants'
|
||||
|
||||
export class BlockNumberProvider {
|
||||
private running: number
|
||||
private web3: Maybe<Web3>
|
||||
private ref: number | undefined
|
||||
private value: Maybe<number>
|
||||
private lastValueTimestamp: Maybe<Date>
|
||||
private readonly interval: number
|
||||
|
||||
constructor(interval = 5000) {
|
||||
this.running = 0
|
||||
this.web3 = null
|
||||
this.ref = undefined
|
||||
this.value = null
|
||||
this.lastValueTimestamp = null
|
||||
this.interval = interval
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
start(web3: Maybe<Web3>) {
|
||||
if (!this.running) {
|
||||
clearTimeout(this.ref)
|
||||
this.web3 = web3
|
||||
this.running = this.running + 1
|
||||
this.fetchLastBlock()
|
||||
} else {
|
||||
this.running = this.running + 1
|
||||
}
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.running = this.running - 1
|
||||
|
||||
if (!this.running) {
|
||||
clearTimeout(this.ref)
|
||||
this.ref = undefined
|
||||
this.web3 = null
|
||||
}
|
||||
}
|
||||
|
||||
get() {
|
||||
return this.value
|
||||
}
|
||||
|
||||
private async fetchLastBlock() {
|
||||
if (!this.web3) return
|
||||
const now = new Date()
|
||||
const distance = differenceInMilliseconds(now, this.lastValueTimestamp || 0)
|
||||
|
||||
if (distance >= this.interval) {
|
||||
this.value = await this.web3.eth.getBlockNumber()
|
||||
this.lastValueTimestamp = now
|
||||
}
|
||||
|
||||
this.ref = setTimeout(() => this.fetchLastBlock(), this.interval)
|
||||
}
|
||||
}
|
||||
|
||||
export const homeBlockNumberProvider = new BlockNumberProvider(HOME_RPC_POLLING_INTERVAL)
|
||||
export const foreignBlockNumberProvider = new BlockNumberProvider(HOME_RPC_POLLING_INTERVAL)
|
||||
29
alm/src/services/ValidatorsCache.ts
Normal file
29
alm/src/services/ValidatorsCache.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { ConfirmationParam } from '../hooks/useMessageConfirmations'
|
||||
|
||||
class ValidatorsCache {
|
||||
private readonly store: { [key: string]: boolean }
|
||||
private readonly dataStore: { [key: string]: ConfirmationParam }
|
||||
|
||||
constructor() {
|
||||
this.store = {}
|
||||
this.dataStore = {}
|
||||
}
|
||||
|
||||
get(key: string) {
|
||||
return this.store[key]
|
||||
}
|
||||
|
||||
set(key: string, value: boolean) {
|
||||
this.store[key] = value
|
||||
}
|
||||
|
||||
getData(key: string) {
|
||||
return this.dataStore[key]
|
||||
}
|
||||
|
||||
setData(key: string, value: ConfirmationParam) {
|
||||
this.dataStore[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
export default new ValidatorsCache()
|
||||
5
alm/src/setupTests.ts
Normal file
5
alm/src/setupTests.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom/extend-expect'
|
||||
78
alm/src/state/StateProvider.tsx
Normal file
78
alm/src/state/StateProvider.tsx
Normal file
@@ -0,0 +1,78 @@
|
||||
import React, { createContext, ReactNode } from 'react'
|
||||
import { useNetwork } from '../hooks/useNetwork'
|
||||
import {
|
||||
HOME_RPC_URL,
|
||||
FOREIGN_RPC_URL,
|
||||
HOME_BRIDGE_ADDRESS,
|
||||
FOREIGN_BRIDGE_ADDRESS,
|
||||
HOME_NETWORK_NAME,
|
||||
FOREIGN_NETWORK_NAME
|
||||
} from '../config/constants'
|
||||
import Web3 from 'web3'
|
||||
import { useBridgeContracts } from '../hooks/useBridgeContracts'
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
|
||||
export interface BaseNetworkParams {
|
||||
chainId: number
|
||||
name: string
|
||||
web3: Maybe<Web3>
|
||||
bridgeAddress: string
|
||||
bridgeContract: Maybe<Contract>
|
||||
}
|
||||
|
||||
export interface StateContext {
|
||||
home: BaseNetworkParams
|
||||
foreign: BaseNetworkParams
|
||||
loading: boolean
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
home: {
|
||||
chainId: 0,
|
||||
name: '',
|
||||
web3: null,
|
||||
bridgeAddress: HOME_BRIDGE_ADDRESS,
|
||||
bridgeContract: null
|
||||
},
|
||||
foreign: {
|
||||
chainId: 0,
|
||||
name: '',
|
||||
web3: null,
|
||||
bridgeAddress: FOREIGN_BRIDGE_ADDRESS,
|
||||
bridgeContract: null
|
||||
},
|
||||
loading: true
|
||||
}
|
||||
|
||||
const StateContext = createContext<StateContext>(initialState)
|
||||
|
||||
export const StateProvider = ({ children }: { children: ReactNode }) => {
|
||||
const homeNetwork = useNetwork(HOME_RPC_URL)
|
||||
const foreignNetwork = useNetwork(FOREIGN_RPC_URL)
|
||||
const { homeBridge, foreignBridge } = useBridgeContracts({
|
||||
homeWeb3: homeNetwork.web3,
|
||||
foreignWeb3: foreignNetwork.web3
|
||||
})
|
||||
|
||||
const value = {
|
||||
home: {
|
||||
bridgeAddress: HOME_BRIDGE_ADDRESS,
|
||||
name: HOME_NETWORK_NAME,
|
||||
bridgeContract: homeBridge,
|
||||
...homeNetwork
|
||||
},
|
||||
foreign: {
|
||||
bridgeAddress: FOREIGN_BRIDGE_ADDRESS,
|
||||
name: FOREIGN_NETWORK_NAME,
|
||||
bridgeContract: foreignBridge,
|
||||
...foreignNetwork
|
||||
},
|
||||
loading: homeNetwork.loading || foreignNetwork.loading
|
||||
}
|
||||
|
||||
return <StateContext.Provider value={value}>{children}</StateContext.Provider>
|
||||
}
|
||||
|
||||
export const useStateProvider = (): StateContext => {
|
||||
return React.useContext(StateContext)
|
||||
}
|
||||
22
alm/src/themes/Dark.tsx
Normal file
22
alm/src/themes/Dark.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
const theme = {
|
||||
backgroundColor: '#121212',
|
||||
fontColor: '#f5f5f5',
|
||||
buttonColor: '#f5f5f5',
|
||||
colorPrimary: '#272727',
|
||||
colorGrey: '#272727',
|
||||
colorLightGrey: '#272727',
|
||||
linkColor: '#ffffff',
|
||||
success: {
|
||||
textColor: '#00c9a7',
|
||||
backgroundColor: '#004d40'
|
||||
},
|
||||
notRequired: {
|
||||
textColor: '#bdbdbd',
|
||||
backgroundColor: '#424242'
|
||||
},
|
||||
failed: {
|
||||
textColor: '#EF5350',
|
||||
backgroundColor: '#4E342E'
|
||||
}
|
||||
}
|
||||
export default theme
|
||||
32
alm/src/themes/GlobalStyle.tsx
Normal file
32
alm/src/themes/GlobalStyle.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { createGlobalStyle } from 'styled-components'
|
||||
|
||||
import theme from './Light'
|
||||
|
||||
type ThemeType = typeof theme
|
||||
|
||||
export const GlobalStyle = createGlobalStyle<{ theme: ThemeType }>`
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
:root {
|
||||
--bg-color: ${props => props.theme.backgroundColor};
|
||||
--font-color: ${props => props.theme.fontColor};
|
||||
--button-color: ${props => props.theme.buttonColor};
|
||||
--color-primary: ${props => props.theme.colorPrimary};
|
||||
--color-grey: ${props => props.theme.colorGrey};
|
||||
--color-lightGrey: ${props => props.theme.colorLightGrey};
|
||||
--link-color: ${props => props.theme.linkColor};
|
||||
--success-color: ${props => props.theme.success.textColor};
|
||||
--success-bg-color: ${props => props.theme.success.backgroundColor};
|
||||
--not-required-color: ${props => props.theme.notRequired.textColor};
|
||||
--not-required-bg-color: ${props => props.theme.notRequired.backgroundColor};
|
||||
--failed-color: ${props => props.theme.failed.textColor};
|
||||
--failed-bg-color: ${props => props.theme.failed.backgroundColor};
|
||||
}
|
||||
`
|
||||
22
alm/src/themes/Light.ts
Normal file
22
alm/src/themes/Light.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
const theme = {
|
||||
backgroundColor: '#FFFFFF',
|
||||
fontColor: 'rgba(0, 0, 0, 0.65)',
|
||||
buttonColor: '#1890ff',
|
||||
colorPrimary: '#BDBDBD',
|
||||
colorGrey: '#1890ff',
|
||||
colorLightGrey: '#1890ff',
|
||||
linkColor: '#1890ff',
|
||||
success: {
|
||||
textColor: '#388E3C',
|
||||
backgroundColor: 'rgba(0,201,167,.1)'
|
||||
},
|
||||
notRequired: {
|
||||
textColor: '#77838f',
|
||||
backgroundColor: 'rgba(119,131,143,.1)'
|
||||
},
|
||||
failed: {
|
||||
textColor: '#de4437',
|
||||
backgroundColor: 'rgba(222,68,55,.1)'
|
||||
}
|
||||
}
|
||||
export default theme
|
||||
68
alm/src/utils/contract.ts
Normal file
68
alm/src/utils/contract.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
|
||||
export const getRequiredBlockConfirmations = async (contract: Contract, blockNumber: number) => {
|
||||
const events = await contract.getPastEvents('RequiredBlockConfirmationChanged', {
|
||||
fromBlock: 0,
|
||||
toBlock: blockNumber
|
||||
})
|
||||
|
||||
let blockConfirmations
|
||||
if (events.length > 0) {
|
||||
// Use the value from last event before the transaction
|
||||
const event = events[events.length - 1]
|
||||
blockConfirmations = event.returnValues.requiredBlockConfirmations
|
||||
} else {
|
||||
// This is a special case where RequiredBlockConfirmationChanged was not emitted during initialization in early versions of AMB
|
||||
// of Sokol - Kovan. In this case the current value is used.
|
||||
blockConfirmations = await contract.methods.requiredBlockConfirmations().call()
|
||||
}
|
||||
return parseInt(blockConfirmations)
|
||||
}
|
||||
|
||||
export const getValidatorAddress = (contract: Contract) => contract.methods.validatorContract().call()
|
||||
|
||||
export const getRequiredSignatures = async (contract: Contract, blockNumber: number) => {
|
||||
const events = await contract.getPastEvents('RequiredSignaturesChanged', {
|
||||
fromBlock: 0,
|
||||
toBlock: blockNumber
|
||||
})
|
||||
|
||||
// Use the value form last event before the transaction
|
||||
const event = events[events.length - 1]
|
||||
const { requiredSignatures } = event.returnValues
|
||||
return parseInt(requiredSignatures)
|
||||
}
|
||||
|
||||
export const getValidatorList = async (contract: Contract, blockNumber: number) => {
|
||||
let currentList: string[] = await contract.methods.validatorList().call()
|
||||
const [added, removed] = await Promise.all([
|
||||
contract.getPastEvents('ValidatorAdded', {
|
||||
fromBlock: blockNumber
|
||||
}),
|
||||
contract.getPastEvents('ValidatorRemoved', {
|
||||
fromBlock: blockNumber
|
||||
})
|
||||
])
|
||||
|
||||
// Ordered desc
|
||||
const orderedEvents = [...added, ...removed].sort(({ blockNumber: prev }, { blockNumber: next }) => next - prev)
|
||||
|
||||
// Stored as a Set to avoid duplicates
|
||||
const validatorList = new Set(currentList)
|
||||
|
||||
orderedEvents.forEach(e => {
|
||||
const { validator } = e.returnValues
|
||||
if (e.event === 'ValidatorRemoved') {
|
||||
validatorList.add(validator)
|
||||
} else if (e.event === 'ValidatorAdded') {
|
||||
validatorList.delete(validator)
|
||||
}
|
||||
})
|
||||
|
||||
return Array.from(validatorList)
|
||||
}
|
||||
|
||||
export const getMessagesSigned = (contract: Contract, hash: string) => contract.methods.messagesSigned(hash).call()
|
||||
|
||||
export const getAffirmationsSigned = (contract: Contract, hash: string) =>
|
||||
contract.methods.affirmationsSigned(hash).call()
|
||||
58
alm/src/utils/executionWaitingForBlocks.ts
Normal file
58
alm/src/utils/executionWaitingForBlocks.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { BlockNumberProvider } from '../services/BlockNumberProvider'
|
||||
import { VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import { EventData } from 'web3-eth-contract'
|
||||
|
||||
export const checkWaitingBlocksForExecution = async (
|
||||
blockProvider: BlockNumberProvider,
|
||||
interval: number,
|
||||
targetBlock: number,
|
||||
collectedSignaturesEvent: EventData,
|
||||
setWaitingBlocksForExecution: Function,
|
||||
setWaitingBlocksForExecutionResolved: Function,
|
||||
setExecutionData: Function,
|
||||
subscriptions: number[]
|
||||
) => {
|
||||
const currentBlock = blockProvider.get()
|
||||
|
||||
if (currentBlock && currentBlock >= targetBlock) {
|
||||
setExecutionData({
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.UNDEFINED,
|
||||
validator: collectedSignaturesEvent.returnValues.authorityResponsibleForRelay,
|
||||
txHash: '',
|
||||
timestamp: 0,
|
||||
executionResult: false
|
||||
})
|
||||
setWaitingBlocksForExecution(false)
|
||||
setWaitingBlocksForExecutionResolved(true)
|
||||
blockProvider.stop()
|
||||
} else {
|
||||
let nextInterval = interval
|
||||
if (!currentBlock) {
|
||||
nextInterval = 500
|
||||
} else {
|
||||
setWaitingBlocksForExecution(true)
|
||||
setExecutionData({
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.WAITING,
|
||||
validator: collectedSignaturesEvent.returnValues.authorityResponsibleForRelay,
|
||||
txHash: '',
|
||||
timestamp: 0,
|
||||
executionResult: false
|
||||
})
|
||||
}
|
||||
const timeoutId = setTimeout(
|
||||
() =>
|
||||
checkWaitingBlocksForExecution(
|
||||
blockProvider,
|
||||
interval,
|
||||
targetBlock,
|
||||
collectedSignaturesEvent,
|
||||
setWaitingBlocksForExecution,
|
||||
setWaitingBlocksForExecutionResolved,
|
||||
setExecutionData,
|
||||
subscriptions
|
||||
),
|
||||
nextInterval
|
||||
)
|
||||
subscriptions.push(timeoutId)
|
||||
}
|
||||
}
|
||||
280
alm/src/utils/explorer.ts
Normal file
280
alm/src/utils/explorer.ts
Normal file
@@ -0,0 +1,280 @@
|
||||
import {
|
||||
EXECUTE_AFFIRMATION_HASH,
|
||||
EXECUTE_SIGNATURES_HASH,
|
||||
FOREIGN_EXPLORER_API,
|
||||
HOME_EXPLORER_API,
|
||||
SUBMIT_SIGNATURE_HASH
|
||||
} from '../config/constants'
|
||||
|
||||
export interface APITransaction {
|
||||
timeStamp: string
|
||||
isError: string
|
||||
input: string
|
||||
to: string
|
||||
hash: string
|
||||
}
|
||||
|
||||
export interface APIPendingTransaction {
|
||||
input: string
|
||||
to: string
|
||||
hash: string
|
||||
}
|
||||
|
||||
export interface PendingTransactionsParams {
|
||||
account: string
|
||||
api: string
|
||||
}
|
||||
|
||||
export interface AccountTransactionsParams {
|
||||
account: string
|
||||
to: string
|
||||
startTimestamp: number
|
||||
endTimestamp: number
|
||||
api: string
|
||||
}
|
||||
|
||||
export interface GetFailedTransactionParams {
|
||||
account: string
|
||||
to: string
|
||||
messageData: string
|
||||
startTimestamp: number
|
||||
endTimestamp: number
|
||||
}
|
||||
|
||||
export interface GetPendingTransactionParams {
|
||||
account: string
|
||||
to: string
|
||||
messageData: string
|
||||
}
|
||||
|
||||
export const fetchAccountTransactionsFromBlockscout = async ({
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
api
|
||||
}: AccountTransactionsParams): Promise<APITransaction[]> => {
|
||||
const url = `${api}?module=account&action=txlist&address=${account}&filterby=from=${account}&to=${to}&starttimestamp=${startTimestamp}&endtimestamp=${endTimestamp}`
|
||||
|
||||
try {
|
||||
const result = await fetch(url).then(res => res.json())
|
||||
if (result.status === '0') {
|
||||
return []
|
||||
}
|
||||
|
||||
return result.result
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export const getBlockByTimestampUrl = (api: string, timestamp: number) =>
|
||||
`${api}&module=block&action=getblocknobytime×tamp=${timestamp}&closest=before`
|
||||
|
||||
export const fetchAccountTransactionsFromEtherscan = async ({
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
api
|
||||
}: AccountTransactionsParams): Promise<APITransaction[]> => {
|
||||
const startBlockUrl = getBlockByTimestampUrl(api, startTimestamp)
|
||||
const endBlockUrl = getBlockByTimestampUrl(api, endTimestamp)
|
||||
let fromBlock = 0
|
||||
let toBlock = 9999999999999
|
||||
try {
|
||||
const [fromBlockResult, toBlockResult] = await Promise.all([
|
||||
fetch(startBlockUrl).then(res => res.json()),
|
||||
fetch(endBlockUrl).then(res => res.json())
|
||||
])
|
||||
|
||||
if (fromBlockResult.status !== '0') {
|
||||
fromBlock = parseInt(fromBlockResult.result)
|
||||
}
|
||||
|
||||
if (toBlockResult.status !== '0') {
|
||||
toBlock = parseInt(toBlockResult.result)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return []
|
||||
}
|
||||
|
||||
const url = `${api}&module=account&action=txlist&address=${account}&startblock=${fromBlock}&endblock=${toBlock}`
|
||||
|
||||
try {
|
||||
const result = await fetch(url).then(res => res.json())
|
||||
|
||||
if (result.status === '0') {
|
||||
return []
|
||||
}
|
||||
|
||||
const toAddressLowerCase = to.toLowerCase()
|
||||
const transactions: APITransaction[] = result.result
|
||||
return transactions.filter(t => t.to.toLowerCase() === toAddressLowerCase)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export const fetchAccountTransactions = (api: string) => {
|
||||
return api.includes('blockscout') ? fetchAccountTransactionsFromBlockscout : fetchAccountTransactionsFromEtherscan
|
||||
}
|
||||
|
||||
export const fetchPendingTransactions = async ({
|
||||
account,
|
||||
api
|
||||
}: PendingTransactionsParams): Promise<APIPendingTransaction[]> => {
|
||||
const url = `${api}?module=account&action=pendingtxlist&address=${account}`
|
||||
|
||||
try {
|
||||
const result = await fetch(url).then(res => res.json())
|
||||
if (result.status === '0') {
|
||||
return []
|
||||
}
|
||||
|
||||
return result.result
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export const getFailedTransactions = async (
|
||||
account: string,
|
||||
to: string,
|
||||
startTimestamp: number,
|
||||
endTimestamp: number,
|
||||
api: string,
|
||||
fetchAccountTransactions: (args: AccountTransactionsParams) => Promise<APITransaction[]>
|
||||
): Promise<APITransaction[]> => {
|
||||
const transactions = await fetchAccountTransactions({ account, to, startTimestamp, endTimestamp, api })
|
||||
|
||||
return transactions.filter(t => t.isError !== '0')
|
||||
}
|
||||
|
||||
export const getSuccessTransactions = async (
|
||||
account: string,
|
||||
to: string,
|
||||
startTimestamp: number,
|
||||
endTimestamp: number,
|
||||
api: string,
|
||||
fetchAccountTransactions: (args: AccountTransactionsParams) => Promise<APITransaction[]>
|
||||
): Promise<APITransaction[]> => {
|
||||
const transactions = await fetchAccountTransactions({ account, to, startTimestamp, endTimestamp, api })
|
||||
|
||||
return transactions.filter(t => t.isError === '0')
|
||||
}
|
||||
|
||||
export const filterValidatorSignatureTransaction = (
|
||||
transactions: APITransaction[],
|
||||
messageData: string
|
||||
): APITransaction[] => {
|
||||
const messageDataValue = messageData.replace('0x', '')
|
||||
return transactions.filter(
|
||||
t =>
|
||||
(t.input.includes(SUBMIT_SIGNATURE_HASH) || t.input.includes(EXECUTE_AFFIRMATION_HASH)) &&
|
||||
t.input.includes(messageDataValue)
|
||||
)
|
||||
}
|
||||
|
||||
export const getValidatorFailedTransactionsForMessage = async ({
|
||||
account,
|
||||
to,
|
||||
messageData,
|
||||
startTimestamp,
|
||||
endTimestamp
|
||||
}: GetFailedTransactionParams): Promise<APITransaction[]> => {
|
||||
const failedTransactions = await getFailedTransactions(
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
HOME_EXPLORER_API,
|
||||
fetchAccountTransactionsFromBlockscout
|
||||
)
|
||||
|
||||
return filterValidatorSignatureTransaction(failedTransactions, messageData)
|
||||
}
|
||||
|
||||
export const getValidatorSuccessTransactionsForMessage = async ({
|
||||
account,
|
||||
to,
|
||||
messageData,
|
||||
startTimestamp,
|
||||
endTimestamp
|
||||
}: GetFailedTransactionParams): Promise<APITransaction[]> => {
|
||||
const transactions = await getSuccessTransactions(
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
HOME_EXPLORER_API,
|
||||
fetchAccountTransactionsFromBlockscout
|
||||
)
|
||||
|
||||
return filterValidatorSignatureTransaction(transactions, messageData)
|
||||
}
|
||||
|
||||
export const getExecutionFailedTransactionForMessage = async ({
|
||||
account,
|
||||
to,
|
||||
messageData,
|
||||
startTimestamp,
|
||||
endTimestamp
|
||||
}: GetFailedTransactionParams): Promise<APITransaction[]> => {
|
||||
const failedTransactions = await getFailedTransactions(
|
||||
account,
|
||||
to,
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
FOREIGN_EXPLORER_API,
|
||||
fetchAccountTransactions(FOREIGN_EXPLORER_API)
|
||||
)
|
||||
|
||||
const messageDataValue = messageData.replace('0x', '')
|
||||
return failedTransactions.filter(t => t.input.includes(EXECUTE_SIGNATURES_HASH) && t.input.includes(messageDataValue))
|
||||
}
|
||||
|
||||
export const getValidatorPendingTransactionsForMessage = async ({
|
||||
account,
|
||||
to,
|
||||
messageData
|
||||
}: GetPendingTransactionParams): Promise<APIPendingTransaction[]> => {
|
||||
const pendingTransactions = await fetchPendingTransactions({
|
||||
account,
|
||||
api: HOME_EXPLORER_API
|
||||
})
|
||||
|
||||
const toAddressLowerCase = to.toLowerCase()
|
||||
const messageDataValue = messageData.replace('0x', '')
|
||||
|
||||
return pendingTransactions.filter(
|
||||
t =>
|
||||
t.to.toLowerCase() === toAddressLowerCase &&
|
||||
(t.input.includes(SUBMIT_SIGNATURE_HASH) || t.input.includes(EXECUTE_AFFIRMATION_HASH)) &&
|
||||
t.input.includes(messageDataValue)
|
||||
)
|
||||
}
|
||||
|
||||
export const getExecutionPendingTransactionsForMessage = async ({
|
||||
account,
|
||||
to,
|
||||
messageData
|
||||
}: GetPendingTransactionParams): Promise<APIPendingTransaction[]> => {
|
||||
const pendingTransactions = await fetchPendingTransactions({
|
||||
account,
|
||||
api: FOREIGN_EXPLORER_API
|
||||
})
|
||||
|
||||
const toAddressLowerCase = to.toLowerCase()
|
||||
const messageDataValue = messageData.replace('0x', '')
|
||||
|
||||
return pendingTransactions.filter(
|
||||
t =>
|
||||
t.to.toLowerCase() === toAddressLowerCase &&
|
||||
t.input.includes(EXECUTE_SIGNATURES_HASH) &&
|
||||
t.input.includes(messageDataValue)
|
||||
)
|
||||
}
|
||||
53
alm/src/utils/getCollectedSignaturesEvent.ts
Normal file
53
alm/src/utils/getCollectedSignaturesEvent.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import Web3 from 'web3'
|
||||
import { Contract, EventData } from 'web3-eth-contract'
|
||||
import { homeBlockNumberProvider } from '../services/BlockNumberProvider'
|
||||
import { BLOCK_RANGE } from '../config/constants'
|
||||
|
||||
export const getCollectedSignaturesEvent = async (
|
||||
web3: Maybe<Web3>,
|
||||
contract: Maybe<Contract>,
|
||||
fromBlock: number,
|
||||
toBlock: number,
|
||||
messageHash: string,
|
||||
setCollectedSignaturesEvent: Function,
|
||||
subscriptions: number[]
|
||||
) => {
|
||||
if (!web3 || !contract) return
|
||||
const currentBlock = homeBlockNumberProvider.get()
|
||||
|
||||
let events: EventData[] = []
|
||||
let securedToBlock = toBlock
|
||||
if (currentBlock) {
|
||||
// prevent errors if the toBlock parameter is bigger than the latest
|
||||
securedToBlock = toBlock >= currentBlock ? currentBlock : toBlock
|
||||
events = await contract.getPastEvents('CollectedSignatures', {
|
||||
fromBlock,
|
||||
toBlock: securedToBlock
|
||||
})
|
||||
}
|
||||
|
||||
const filteredEvents = events.filter(e => e.returnValues.messageHash === messageHash)
|
||||
|
||||
if (filteredEvents.length) {
|
||||
const event = filteredEvents[0]
|
||||
setCollectedSignaturesEvent(event)
|
||||
homeBlockNumberProvider.stop()
|
||||
} else {
|
||||
const newFromBlock = currentBlock ? securedToBlock : fromBlock
|
||||
const newToBlock = currentBlock ? toBlock + BLOCK_RANGE : toBlock
|
||||
const timeoutId = setTimeout(
|
||||
() =>
|
||||
getCollectedSignaturesEvent(
|
||||
web3,
|
||||
contract,
|
||||
newFromBlock,
|
||||
newToBlock,
|
||||
messageHash,
|
||||
setCollectedSignaturesEvent,
|
||||
subscriptions
|
||||
),
|
||||
500
|
||||
)
|
||||
subscriptions.push(timeoutId)
|
||||
}
|
||||
}
|
||||
317
alm/src/utils/getConfirmationsForTx.ts
Normal file
317
alm/src/utils/getConfirmationsForTx.ts
Normal file
@@ -0,0 +1,317 @@
|
||||
import Web3 from 'web3'
|
||||
import { Contract } from 'web3-eth-contract'
|
||||
import validatorsCache from '../services/ValidatorsCache'
|
||||
import {
|
||||
CACHE_KEY_FAILED,
|
||||
CACHE_KEY_SUCCESS,
|
||||
HOME_RPC_POLLING_INTERVAL,
|
||||
ONE_DAY_TIMESTAMP,
|
||||
VALIDATOR_CONFIRMATION_STATUS
|
||||
} from '../config/constants'
|
||||
import {
|
||||
GetFailedTransactionParams,
|
||||
APITransaction,
|
||||
APIPendingTransaction,
|
||||
GetPendingTransactionParams
|
||||
} from './explorer'
|
||||
import { BasicConfirmationParam, ConfirmationParam } from '../hooks/useMessageConfirmations'
|
||||
|
||||
export const getValidatorConfirmation = (
|
||||
web3: Web3,
|
||||
hashMsg: string,
|
||||
bridgeContract: Contract,
|
||||
confirmationContractMethod: Function
|
||||
) => async (validator: string): Promise<BasicConfirmationParam> => {
|
||||
const hashSenderMsg = web3.utils.soliditySha3Raw(validator, hashMsg)
|
||||
|
||||
const signatureFromCache = validatorsCache.get(hashSenderMsg)
|
||||
if (signatureFromCache) {
|
||||
return {
|
||||
validator,
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
const confirmed = await confirmationContractMethod(bridgeContract, hashSenderMsg)
|
||||
const status = confirmed ? VALIDATOR_CONFIRMATION_STATUS.SUCCESS : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||
|
||||
// If validator confirmed signature, we cache the result to avoid doing future requests for a result that won't change
|
||||
if (confirmed) {
|
||||
validatorsCache.set(hashSenderMsg, confirmed)
|
||||
}
|
||||
|
||||
return {
|
||||
validator,
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
export const getValidatorSuccessTransaction = (
|
||||
bridgeContract: Contract,
|
||||
messageData: string,
|
||||
timestamp: number,
|
||||
getSuccessTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
||||
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
||||
const { validator } = validatorData
|
||||
const validatorCacheKey = `${CACHE_KEY_SUCCESS}${validatorData.validator}-${messageData}`
|
||||
const fromCache = validatorsCache.getData(validatorCacheKey)
|
||||
|
||||
if (fromCache && fromCache.txHash) {
|
||||
return fromCache
|
||||
}
|
||||
|
||||
const transactions = await getSuccessTransactions({
|
||||
account: validatorData.validator,
|
||||
to: bridgeContract.options.address,
|
||||
messageData,
|
||||
startTimestamp: timestamp,
|
||||
endTimestamp: timestamp + ONE_DAY_TIMESTAMP
|
||||
})
|
||||
|
||||
let txHashTimestamp = 0
|
||||
let txHash = ''
|
||||
const status = VALIDATOR_CONFIRMATION_STATUS.SUCCESS
|
||||
|
||||
if (transactions.length > 0) {
|
||||
const tx = transactions[0]
|
||||
txHashTimestamp = parseInt(tx.timeStamp)
|
||||
txHash = tx.hash
|
||||
|
||||
// cache the result
|
||||
validatorsCache.setData(validatorCacheKey, {
|
||||
validator,
|
||||
status,
|
||||
txHash,
|
||||
timestamp: txHashTimestamp
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
validator,
|
||||
status,
|
||||
txHash,
|
||||
timestamp: txHashTimestamp
|
||||
}
|
||||
}
|
||||
|
||||
export const getValidatorFailedTransaction = (
|
||||
bridgeContract: Contract,
|
||||
messageData: string,
|
||||
timestamp: number,
|
||||
getFailedTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
||||
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
||||
const validatorCacheKey = `${CACHE_KEY_FAILED}${validatorData.validator}-${messageData}`
|
||||
const failedFromCache = validatorsCache.getData(validatorCacheKey)
|
||||
|
||||
if (failedFromCache && failedFromCache.txHash) {
|
||||
return failedFromCache
|
||||
}
|
||||
|
||||
const failedTransactions = await getFailedTransactions({
|
||||
account: validatorData.validator,
|
||||
to: bridgeContract.options.address,
|
||||
messageData,
|
||||
startTimestamp: timestamp,
|
||||
endTimestamp: timestamp + ONE_DAY_TIMESTAMP
|
||||
})
|
||||
const newStatus =
|
||||
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.FAILED : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||
|
||||
let txHashTimestamp = 0
|
||||
let txHash = ''
|
||||
// If validator signature failed, we cache the result to avoid doing future requests for a result that won't change
|
||||
if (failedTransactions.length > 0) {
|
||||
const failedTx = failedTransactions[0]
|
||||
txHashTimestamp = parseInt(failedTx.timeStamp)
|
||||
txHash = failedTx.hash
|
||||
|
||||
validatorsCache.setData(validatorCacheKey, {
|
||||
validator: validatorData.validator,
|
||||
status: newStatus,
|
||||
txHash,
|
||||
timestamp: txHashTimestamp
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
validator: validatorData.validator,
|
||||
status: newStatus,
|
||||
txHash,
|
||||
timestamp: txHashTimestamp
|
||||
}
|
||||
}
|
||||
|
||||
export const getValidatorPendingTransaction = (
|
||||
bridgeContract: Contract,
|
||||
messageData: string,
|
||||
getPendingTransactions: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>
|
||||
) => async (validatorData: BasicConfirmationParam): Promise<ConfirmationParam> => {
|
||||
const failedTransactions = await getPendingTransactions({
|
||||
account: validatorData.validator,
|
||||
to: bridgeContract.options.address,
|
||||
messageData
|
||||
})
|
||||
|
||||
const newStatus =
|
||||
failedTransactions.length > 0 ? VALIDATOR_CONFIRMATION_STATUS.PENDING : VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||
|
||||
let timestamp = 0
|
||||
let txHash = ''
|
||||
|
||||
if (failedTransactions.length > 0) {
|
||||
const failedTx = failedTransactions[0]
|
||||
timestamp = Math.floor(new Date().getTime() / 1000.0)
|
||||
txHash = failedTx.hash
|
||||
}
|
||||
|
||||
return {
|
||||
validator: validatorData.validator,
|
||||
status: newStatus,
|
||||
txHash,
|
||||
timestamp
|
||||
}
|
||||
}
|
||||
|
||||
export const getConfirmationsForTx = async (
|
||||
messageData: string,
|
||||
web3: Maybe<Web3>,
|
||||
validatorList: string[],
|
||||
bridgeContract: Maybe<Contract>,
|
||||
confirmationContractMethod: Function,
|
||||
setResult: Function,
|
||||
requiredSignatures: number,
|
||||
setSignatureCollected: Function,
|
||||
waitingBlocksResolved: boolean,
|
||||
subscriptions: number[],
|
||||
timestamp: number,
|
||||
getFailedTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>,
|
||||
setFailedConfirmations: Function,
|
||||
getPendingTransactions: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>,
|
||||
setPendingConfirmations: Function,
|
||||
getSuccessTransactions: (args: GetFailedTransactionParams) => Promise<APITransaction[]>
|
||||
) => {
|
||||
if (!web3 || !validatorList || !validatorList.length || !bridgeContract || !waitingBlocksResolved) return
|
||||
|
||||
// If all the information was not collected, then it should retry
|
||||
let shouldRetry = false
|
||||
const hashMsg = web3.utils.soliditySha3Raw(messageData)
|
||||
let validatorConfirmations = await Promise.all(
|
||||
validatorList.map(getValidatorConfirmation(web3, hashMsg, bridgeContract, confirmationContractMethod))
|
||||
)
|
||||
|
||||
const successConfirmations = validatorConfirmations.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
|
||||
|
||||
const notSuccessConfirmations = validatorConfirmations.filter(c => c.status !== VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
|
||||
|
||||
// If signatures not collected, it needs to retry in the next blocks
|
||||
if (successConfirmations.length !== requiredSignatures) {
|
||||
// Check if confirmation is pending
|
||||
const validatorPendingConfirmationsChecks = await Promise.all(
|
||||
notSuccessConfirmations.map(getValidatorPendingTransaction(bridgeContract, messageData, getPendingTransactions))
|
||||
)
|
||||
const validatorPendingConfirmations = validatorPendingConfirmationsChecks.filter(
|
||||
c => c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING
|
||||
)
|
||||
|
||||
validatorPendingConfirmations.forEach(validatorData => {
|
||||
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator)
|
||||
validatorConfirmations[index] = validatorData
|
||||
})
|
||||
|
||||
if (validatorPendingConfirmations.length > 0) {
|
||||
setPendingConfirmations(true)
|
||||
}
|
||||
|
||||
const undefinedConfirmations = validatorConfirmations.filter(
|
||||
c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED
|
||||
)
|
||||
// Check if confirmation failed
|
||||
const validatorFailedConfirmationsChecks = await Promise.all(
|
||||
undefinedConfirmations.map(
|
||||
getValidatorFailedTransaction(bridgeContract, messageData, timestamp, getFailedTransactions)
|
||||
)
|
||||
)
|
||||
const validatorFailedConfirmations = validatorFailedConfirmationsChecks.filter(
|
||||
c => c.status === VALIDATOR_CONFIRMATION_STATUS.FAILED
|
||||
)
|
||||
validatorFailedConfirmations.forEach(validatorData => {
|
||||
const index = validatorConfirmations.findIndex(e => e.validator === validatorData.validator)
|
||||
validatorConfirmations[index] = validatorData
|
||||
})
|
||||
const messageConfirmationsFailed = validatorFailedConfirmations.length > validatorList.length - requiredSignatures
|
||||
if (messageConfirmationsFailed) {
|
||||
setFailedConfirmations(true)
|
||||
}
|
||||
|
||||
const missingConfirmations = validatorConfirmations.filter(
|
||||
c => c.status === VALIDATOR_CONFIRMATION_STATUS.UNDEFINED || c.status === VALIDATOR_CONFIRMATION_STATUS.PENDING
|
||||
)
|
||||
|
||||
if (missingConfirmations.length > 0) {
|
||||
shouldRetry = true
|
||||
}
|
||||
} else {
|
||||
// If signatures collected, it should set other signatures as not required
|
||||
const notRequiredConfirmations = notSuccessConfirmations.map(c => ({
|
||||
validator: c.validator,
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.NOT_REQUIRED
|
||||
}))
|
||||
|
||||
validatorConfirmations = [...successConfirmations, ...notRequiredConfirmations]
|
||||
setSignatureCollected(true)
|
||||
}
|
||||
|
||||
// Set confirmations to update UI and continue requesting the transactions for the signatures
|
||||
setResult(validatorConfirmations)
|
||||
|
||||
// get transactions from success signatures
|
||||
const successConfirmationWithData = await Promise.all(
|
||||
validatorConfirmations
|
||||
.filter(c => c.status === VALIDATOR_CONFIRMATION_STATUS.SUCCESS)
|
||||
.map(getValidatorSuccessTransaction(bridgeContract, messageData, timestamp, getSuccessTransactions))
|
||||
)
|
||||
|
||||
const successConfirmationWithTxFound = successConfirmationWithData.filter(v => v.txHash !== '')
|
||||
|
||||
const updatedValidatorConfirmations = [...validatorConfirmations]
|
||||
|
||||
if (successConfirmationWithTxFound.length > 0) {
|
||||
successConfirmationWithTxFound.forEach(validatorData => {
|
||||
const index = updatedValidatorConfirmations.findIndex(e => e.validator === validatorData.validator)
|
||||
updatedValidatorConfirmations[index] = validatorData
|
||||
})
|
||||
}
|
||||
|
||||
setResult(updatedValidatorConfirmations)
|
||||
|
||||
// Retry if not all transaction were found for validator confirmations
|
||||
if (successConfirmationWithTxFound.length < successConfirmationWithData.length) {
|
||||
shouldRetry = true
|
||||
}
|
||||
|
||||
if (shouldRetry) {
|
||||
const timeoutId = setTimeout(
|
||||
() =>
|
||||
getConfirmationsForTx(
|
||||
messageData,
|
||||
web3,
|
||||
validatorList,
|
||||
bridgeContract,
|
||||
confirmationContractMethod,
|
||||
setResult,
|
||||
requiredSignatures,
|
||||
setSignatureCollected,
|
||||
waitingBlocksResolved,
|
||||
subscriptions,
|
||||
timestamp,
|
||||
getFailedTransactions,
|
||||
setFailedConfirmations,
|
||||
getPendingTransactions,
|
||||
setPendingConfirmations,
|
||||
getSuccessTransactions
|
||||
),
|
||||
HOME_RPC_POLLING_INTERVAL
|
||||
)
|
||||
subscriptions.push(timeoutId)
|
||||
}
|
||||
}
|
||||
137
alm/src/utils/getFinalizationEvent.ts
Normal file
137
alm/src/utils/getFinalizationEvent.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { Contract, EventData } from 'web3-eth-contract'
|
||||
import Web3 from 'web3'
|
||||
import { CACHE_KEY_EXECUTION_FAILED, THREE_DAYS_TIMESTAMP, VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import { ExecutionData } from '../hooks/useMessageConfirmations'
|
||||
import {
|
||||
APIPendingTransaction,
|
||||
APITransaction,
|
||||
GetFailedTransactionParams,
|
||||
GetPendingTransactionParams
|
||||
} from './explorer'
|
||||
import { getBlock, MessageObject } from './web3'
|
||||
import validatorsCache from '../services/ValidatorsCache'
|
||||
|
||||
export const getFinalizationEvent = async (
|
||||
contract: Maybe<Contract>,
|
||||
eventName: string,
|
||||
web3: Maybe<Web3>,
|
||||
setResult: React.Dispatch<React.SetStateAction<ExecutionData>>,
|
||||
waitingBlocksResolved: boolean,
|
||||
message: MessageObject,
|
||||
interval: number,
|
||||
subscriptions: number[],
|
||||
timestamp: number,
|
||||
collectedSignaturesEvent: Maybe<EventData>,
|
||||
getFailedExecution: (args: GetFailedTransactionParams) => Promise<APITransaction[]>,
|
||||
setFailedExecution: Function,
|
||||
getPendingExecution: (args: GetPendingTransactionParams) => Promise<APIPendingTransaction[]>,
|
||||
setPendingExecution: Function
|
||||
) => {
|
||||
if (!contract || !web3 || !waitingBlocksResolved) return
|
||||
// Since it filters by the message id, only one event will be fetched
|
||||
// so there is no need to limit the range of the block to reduce the network traffic
|
||||
const events: EventData[] = await contract.getPastEvents(eventName, {
|
||||
fromBlock: 0,
|
||||
toBlock: 'latest',
|
||||
filter: {
|
||||
messageId: message.id
|
||||
}
|
||||
})
|
||||
if (events.length > 0) {
|
||||
const event = events[0]
|
||||
const [txReceipt, block] = await Promise.all([
|
||||
web3.eth.getTransactionReceipt(event.transactionHash),
|
||||
getBlock(web3, event.blockNumber)
|
||||
])
|
||||
|
||||
const blockTimestamp = typeof block.timestamp === 'string' ? parseInt(block.timestamp) : block.timestamp
|
||||
const validatorAddress = web3.utils.toChecksumAddress(txReceipt.from)
|
||||
|
||||
setResult({
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.SUCCESS,
|
||||
validator: validatorAddress,
|
||||
txHash: event.transactionHash,
|
||||
timestamp: blockTimestamp,
|
||||
executionResult: event.returnValues.status
|
||||
})
|
||||
} else {
|
||||
// If event is defined, it means it is a message from Home to Foreign
|
||||
if (collectedSignaturesEvent) {
|
||||
const validator = collectedSignaturesEvent.returnValues.authorityResponsibleForRelay
|
||||
|
||||
const pendingTransactions = await getPendingExecution({
|
||||
account: validator,
|
||||
messageData: message.data,
|
||||
to: contract.options.address
|
||||
})
|
||||
|
||||
// If the transaction is pending it sets the status and avoid making the request for failed transactions
|
||||
if (pendingTransactions.length > 0) {
|
||||
const pendingTx = pendingTransactions[0]
|
||||
|
||||
const nowTimestamp = Math.floor(new Date().getTime() / 1000.0)
|
||||
|
||||
setResult({
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.PENDING,
|
||||
validator: validator,
|
||||
txHash: pendingTx.hash,
|
||||
timestamp: nowTimestamp,
|
||||
executionResult: false
|
||||
})
|
||||
setPendingExecution(true)
|
||||
} else {
|
||||
const validatorExecutionCacheKey = `${CACHE_KEY_EXECUTION_FAILED}${validator}-${message.id}`
|
||||
const failedFromCache = validatorsCache.get(validatorExecutionCacheKey)
|
||||
|
||||
if (!failedFromCache) {
|
||||
const failedTransactions = await getFailedExecution({
|
||||
account: validator,
|
||||
to: contract.options.address,
|
||||
messageData: message.data,
|
||||
startTimestamp: timestamp,
|
||||
endTimestamp: timestamp + THREE_DAYS_TIMESTAMP
|
||||
})
|
||||
|
||||
if (failedTransactions.length > 0) {
|
||||
const failedTx = failedTransactions[0]
|
||||
|
||||
// If validator execution failed, we cache the result to avoid doing future requests for a result that won't change
|
||||
validatorsCache.set(validatorExecutionCacheKey, true)
|
||||
|
||||
const timestamp = parseInt(failedTx.timeStamp)
|
||||
setResult({
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.FAILED,
|
||||
validator: validator,
|
||||
txHash: failedTx.hash,
|
||||
timestamp,
|
||||
executionResult: false
|
||||
})
|
||||
setFailedExecution(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const timeoutId = setTimeout(
|
||||
() =>
|
||||
getFinalizationEvent(
|
||||
contract,
|
||||
eventName,
|
||||
web3,
|
||||
setResult,
|
||||
waitingBlocksResolved,
|
||||
message,
|
||||
interval,
|
||||
subscriptions,
|
||||
timestamp,
|
||||
collectedSignaturesEvent,
|
||||
getFailedExecution,
|
||||
setFailedExecution,
|
||||
getPendingExecution,
|
||||
setPendingExecution
|
||||
),
|
||||
interval
|
||||
)
|
||||
subscriptions.push(timeoutId)
|
||||
}
|
||||
}
|
||||
41
alm/src/utils/networks.ts
Normal file
41
alm/src/utils/networks.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { formatDistance } from 'date-fns'
|
||||
import { CONFIRMATIONS_STATUS_DESCRIPTION, TRANSACTION_STATUS_DESCRIPTION } from '../config/descriptions'
|
||||
import { FOREIGN_EXPLORER_TX_TEMPLATE, HOME_EXPLORER_TX_TEMPLATE } from '../config/constants'
|
||||
|
||||
export const validTxHash = (txHash: string) => /^0x[a-fA-F0-9]{64}$/.test(txHash)
|
||||
|
||||
export const formatTxHash = (txHash: string) => `${txHash.substring(0, 6)}...${txHash.substring(txHash.length - 4)}`
|
||||
|
||||
export const getExplorerTxUrl = (txHash: string, isHome: boolean) => {
|
||||
const template = isHome ? HOME_EXPLORER_TX_TEMPLATE : FOREIGN_EXPLORER_TX_TEMPLATE
|
||||
return template.replace('%s', txHash)
|
||||
}
|
||||
|
||||
export const formatTxHashExtended = (txHash: string) =>
|
||||
`${txHash.substring(0, 10)}...${txHash.substring(txHash.length - 8)}`
|
||||
|
||||
export const formatTimestamp = (timestamp: number): string => {
|
||||
const txDate = new Date(0).setUTCSeconds(timestamp)
|
||||
return formatDistance(txDate, new Date(), {
|
||||
addSuffix: true
|
||||
})
|
||||
}
|
||||
|
||||
export const getTransactionStatusDescription = (status: string, timestamp: Maybe<number> = null) => {
|
||||
let description = TRANSACTION_STATUS_DESCRIPTION[status]
|
||||
|
||||
if (timestamp) {
|
||||
description = description.replace('%t', formatTimestamp(timestamp))
|
||||
}
|
||||
|
||||
return description
|
||||
}
|
||||
|
||||
export const getConfirmationsStatusDescription = (status: string, home: string, foreign: string) => {
|
||||
let description = CONFIRMATIONS_STATUS_DESCRIPTION[status]
|
||||
|
||||
description = description.replace('%homeChain', home)
|
||||
description = description.replace('%foreignChain', foreign)
|
||||
|
||||
return description
|
||||
}
|
||||
50
alm/src/utils/signatureWaitingForBlocks.ts
Normal file
50
alm/src/utils/signatureWaitingForBlocks.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { VALIDATOR_CONFIRMATION_STATUS } from '../config/constants'
|
||||
import { BlockNumberProvider } from '../services/BlockNumberProvider'
|
||||
|
||||
export const checkSignaturesWaitingForBLocks = async (
|
||||
targetBlock: number,
|
||||
setWaitingStatus: Function,
|
||||
setWaitingBlocksResolved: Function,
|
||||
validatorList: string[],
|
||||
setConfirmations: Function,
|
||||
blockProvider: BlockNumberProvider,
|
||||
interval: number,
|
||||
subscriptions: number[]
|
||||
) => {
|
||||
const currentBlock = blockProvider.get()
|
||||
|
||||
if (currentBlock && currentBlock >= targetBlock) {
|
||||
setWaitingStatus(false)
|
||||
setWaitingBlocksResolved(true)
|
||||
blockProvider.stop()
|
||||
} else {
|
||||
let nextInterval = interval
|
||||
if (!currentBlock) {
|
||||
nextInterval = 500
|
||||
} else {
|
||||
const validatorsWaiting = validatorList.map(validator => {
|
||||
return {
|
||||
validator,
|
||||
status: VALIDATOR_CONFIRMATION_STATUS.WAITING
|
||||
}
|
||||
})
|
||||
setWaitingStatus(true)
|
||||
setConfirmations(validatorsWaiting)
|
||||
}
|
||||
const timeoutId = setTimeout(
|
||||
() =>
|
||||
checkSignaturesWaitingForBLocks(
|
||||
targetBlock,
|
||||
setWaitingStatus,
|
||||
setWaitingBlocksResolved,
|
||||
validatorList,
|
||||
setConfirmations,
|
||||
blockProvider,
|
||||
interval,
|
||||
subscriptions
|
||||
),
|
||||
nextInterval
|
||||
)
|
||||
subscriptions.push(timeoutId)
|
||||
}
|
||||
}
|
||||
63
alm/src/utils/web3.ts
Normal file
63
alm/src/utils/web3.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import Web3 from 'web3'
|
||||
import { BlockTransactionString } from 'web3-eth'
|
||||
import { TransactionReceipt } from 'web3-eth'
|
||||
import { AbiItem } from 'web3-utils'
|
||||
import memoize from 'fast-memoize'
|
||||
import promiseRetry from 'promise-retry'
|
||||
import { HOME_AMB_ABI, FOREIGN_AMB_ABI } from '../abis'
|
||||
|
||||
export interface MessageObject {
|
||||
id: string
|
||||
data: string
|
||||
}
|
||||
|
||||
const rawGetWeb3 = (url: string) => new Web3(new Web3.providers.HttpProvider(url))
|
||||
const memoized = memoize(rawGetWeb3)
|
||||
|
||||
export const getWeb3 = (url: string) => memoized(url)
|
||||
|
||||
export const filterEventsByAbi = (
|
||||
txReceipt: TransactionReceipt,
|
||||
web3: Web3,
|
||||
bridgeAddress: string,
|
||||
eventAbi: AbiItem
|
||||
): MessageObject[] => {
|
||||
const eventHash = web3.eth.abi.encodeEventSignature(eventAbi)
|
||||
const events = txReceipt.logs.filter(e => e.address === bridgeAddress && e.topics[0] === eventHash)
|
||||
|
||||
return events.map(e => {
|
||||
let decodedLogs: { [p: string]: string } = {
|
||||
messageId: '',
|
||||
encodedData: ''
|
||||
}
|
||||
if (eventAbi && eventAbi.inputs && eventAbi.inputs.length) {
|
||||
decodedLogs = web3.eth.abi.decodeLog(eventAbi.inputs, e.data, [e.topics[1]])
|
||||
}
|
||||
return { id: decodedLogs.messageId, data: decodedLogs.encodedData }
|
||||
})
|
||||
}
|
||||
|
||||
export const getHomeMessagesFromReceipt = (txReceipt: TransactionReceipt, web3: Web3, bridgeAddress: string) => {
|
||||
const UserRequestForSignatureAbi: AbiItem = HOME_AMB_ABI.filter(
|
||||
(e: AbiItem) => e.type === 'event' && e.name === 'UserRequestForSignature'
|
||||
)[0]
|
||||
return filterEventsByAbi(txReceipt, web3, bridgeAddress, UserRequestForSignatureAbi)
|
||||
}
|
||||
|
||||
export const getForeignMessagesFromReceipt = (txReceipt: TransactionReceipt, web3: Web3, bridgeAddress: string) => {
|
||||
const userRequestForAffirmationAbi: AbiItem = FOREIGN_AMB_ABI.filter(
|
||||
(e: AbiItem) => e.type === 'event' && e.name === 'UserRequestForAffirmation'
|
||||
)[0]
|
||||
return filterEventsByAbi(txReceipt, web3, bridgeAddress, userRequestForAffirmationAbi)
|
||||
}
|
||||
|
||||
// In some rare cases the block data is not available yet for the block of a new event detected
|
||||
// so this logic retry to get the block in case it fails
|
||||
export const getBlock = async (web3: Web3, blockNumber: number): Promise<BlockTransactionString> =>
|
||||
promiseRetry(async retry => {
|
||||
const result = await web3.eth.getBlock(blockNumber)
|
||||
if (!result) {
|
||||
return retry('Error getting block data')
|
||||
}
|
||||
return result
|
||||
})
|
||||
25
alm/tsconfig.json
Normal file
25
alm/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"
|
||||
]
|
||||
}
|
||||
@@ -16,8 +16,7 @@
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"typescript": "3.5.1",
|
||||
"web3": "1.0.0-beta.55"
|
||||
"typescript": "3.5.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
||||
53
burner-wallet-plugin/testing/src/LocalhostGateway.ts
Normal file
53
burner-wallet-plugin/testing/src/LocalhostGateway.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { Gateway } from '@burner-wallet/core/gateways'
|
||||
import Web3 from 'web3'
|
||||
|
||||
export default class LocalhostGateway extends Gateway {
|
||||
private readonly providers: object
|
||||
private readonly providerStrings: { [id: string]: string }
|
||||
constructor() {
|
||||
super()
|
||||
this.providerStrings = {
|
||||
'111': 'http://localhost:8545',
|
||||
'1337': 'http://localhost:8546'
|
||||
}
|
||||
this.providers = {}
|
||||
}
|
||||
|
||||
isAvailable() {
|
||||
return true
|
||||
}
|
||||
|
||||
getNetworks() {
|
||||
return ['111', '1337']
|
||||
}
|
||||
|
||||
_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 LocalhostGateway`)
|
||||
}
|
||||
|
||||
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('LocalhostGateway does not support this network'))
|
||||
}
|
||||
|
||||
this._provider(network).send(payload, (err, response) => {
|
||||
if (err) {
|
||||
return reject(err)
|
||||
}
|
||||
return resolve(response.result)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,13 +6,23 @@ 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 {
|
||||
Mediator,
|
||||
sPOA,
|
||||
ERC677Asset,
|
||||
TokenBridgeGateway,
|
||||
NativeMediatorAsset,
|
||||
MediatorErcToNative,
|
||||
BridgeableERC20Asset
|
||||
} from '@poanet/tokenbridge-bw-exchange'
|
||||
import MetamaskPlugin from '@burner-wallet/metamask-plugin'
|
||||
import LocalhostGateway from './LocalhostGateway'
|
||||
|
||||
let assetIdAtHome = 'assetAtHome'
|
||||
const assetIdAtForeign = 'assetAtForeign'
|
||||
let assetAtHome: Asset
|
||||
let assetAtForeign: Asset
|
||||
let testBridge: Mediator
|
||||
|
||||
if (process.env.REACT_APP_MODE === 'AMB_NATIVE_TO_ERC677') {
|
||||
sPOA.setMediatorAddress(process.env.REACT_APP_HOME_MEDIATOR_ADDRESS)
|
||||
@@ -28,8 +38,16 @@ if (process.env.REACT_APP_MODE === 'AMB_NATIVE_TO_ERC677') {
|
||||
// @ts-ignore
|
||||
address: process.env.REACT_APP_FOREIGN_TOKEN_ADDRESS
|
||||
})
|
||||
} else {
|
||||
// process.env.REACT_APP_MODE === 'AMB_ERC677_TO_ERC677'
|
||||
|
||||
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
|
||||
})
|
||||
} else if (process.env.REACT_APP_MODE === 'AMB_ERC677_TO_ERC677') {
|
||||
assetAtHome = new ERC677Asset({
|
||||
id: 'assetAtHome',
|
||||
// @ts-ignore
|
||||
@@ -49,20 +67,54 @@ if (process.env.REACT_APP_MODE === 'AMB_NATIVE_TO_ERC677') {
|
||||
// @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
|
||||
})
|
||||
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
|
||||
})
|
||||
} else {
|
||||
// process.env.REACT_APP_MODE === 'AMB_ERC20_TO_NATIVE'
|
||||
assetAtHome = new NativeMediatorAsset({
|
||||
id: assetIdAtHome,
|
||||
name: 'qDAI',
|
||||
network: process.env.REACT_APP_HOME_NETWORK as string,
|
||||
mediatorAddress: process.env.REACT_APP_HOME_MEDIATOR_ADDRESS
|
||||
})
|
||||
|
||||
assetAtForeign = new BridgeableERC20Asset({
|
||||
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,
|
||||
// @ts-ignore
|
||||
bridgeAddress: process.env.REACT_APP_FOREIGN_MEDIATOR_ADDRESS
|
||||
})
|
||||
|
||||
testBridge = new MediatorErcToNative({
|
||||
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)],
|
||||
gateways: [
|
||||
new InjectedGateway(),
|
||||
new LocalhostGateway(),
|
||||
new TokenBridgeGateway(),
|
||||
new InfuraGateway(process.env.REACT_APP_INFURA_KEY)
|
||||
],
|
||||
assets: [assetAtHome, assetAtForeign]
|
||||
})
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"noImplicitAny": false,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"include": [
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
import { ERC20Asset } from '@burner-wallet/assets'
|
||||
import { MEDIATOR_ABI, constants } from '../../utils'
|
||||
import { toBN } from 'web3-utils'
|
||||
|
||||
interface BridgeableERC20Constructor {
|
||||
abi?: object
|
||||
address: string
|
||||
id: string
|
||||
name: string
|
||||
network: string
|
||||
bridgeAddress: string
|
||||
}
|
||||
|
||||
export default class BridgeableERC20Asset extends ERC20Asset {
|
||||
protected bridgeAddress: string
|
||||
private _bridge
|
||||
|
||||
constructor({ bridgeAddress, ...params }: BridgeableERC20Constructor) {
|
||||
super({ ...params })
|
||||
this.bridgeAddress = bridgeAddress.toLowerCase()
|
||||
}
|
||||
|
||||
getBridgeContract() {
|
||||
if (!this._bridge) {
|
||||
const Contract = this.getWeb3().eth.Contract
|
||||
this._bridge = new Contract(MEDIATOR_ABI, this.bridgeAddress)
|
||||
}
|
||||
return this._bridge
|
||||
}
|
||||
|
||||
async _send({ from, to, value }) {
|
||||
if (to.toLowerCase() === this.bridgeAddress) {
|
||||
const allowance = await this.allowance(from, to)
|
||||
if (toBN(allowance).lt(toBN(value))) {
|
||||
await this.approve(from, to, value)
|
||||
}
|
||||
const receipt = await this.getBridgeContract()
|
||||
.methods.relayTokens(from, value)
|
||||
.send({ from })
|
||||
const transferLog = Object.values(receipt.events as object).find(
|
||||
e => e.raw.topics[0] === constants.TRANSFER_TOPIC
|
||||
)
|
||||
return {
|
||||
...receipt,
|
||||
txHash: receipt.transactionHash,
|
||||
id: `${receipt.transactionHash}-${transferLog.logIndex}`
|
||||
}
|
||||
}
|
||||
return super._send({ from, to, value })
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { ERC20Asset } from '@burner-wallet/assets'
|
||||
import { ERC677_ABI } from '../../utils'
|
||||
import { ERC677_ABI, constants } from '../../utils'
|
||||
|
||||
const TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef'
|
||||
const BLOCK_LOOKBACK = 250
|
||||
|
||||
interface ERC677Constructor {
|
||||
@@ -45,7 +44,7 @@ export default class ERC677Asset extends ERC20Asset {
|
||||
const allTransferEvents = await this.getContract().getPastEvents('allEvents', {
|
||||
fromBlock: block,
|
||||
toBlock: currentBlock,
|
||||
topics: [TRANSFER_TOPIC]
|
||||
topics: [constants.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())
|
||||
|
||||
@@ -2,6 +2,8 @@ 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 BridgeableERC20Asset } from './assets/BridgeableERC20Asset'
|
||||
export { default as NativeMediatorAsset } from './assets/NativeMediatorAsset'
|
||||
export { default as TokenBridgeGateway } from './gateways/TokenBridgeGateway'
|
||||
export { default as Mediator } from './pairs/Mediator'
|
||||
export { default as MediatorErcToNative } from './pairs/MediatorErcToNative'
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import BN from 'bn.js'
|
||||
import { EstimateReturn, ValueTypes } from '@burner-wallet/exchange'
|
||||
import { constants } from '../../utils'
|
||||
import { MEDIATOR_ERC_TO_NATIVE_ABI } from '../../utils'
|
||||
import { default as Mediator } from './Mediator'
|
||||
import { fromWei, toBN } from 'web3-utils'
|
||||
|
||||
export default class MediatorErcToNative extends Mediator {
|
||||
constructor(params) {
|
||||
super(params)
|
||||
}
|
||||
|
||||
async estimateAtoB(value: ValueTypes): Promise<EstimateReturn> {
|
||||
return this.estimateWithFee(constants.HOME_TO_FOREIGN_FEE_TYPE, value)
|
||||
}
|
||||
|
||||
async estimateBtoA(value: ValueTypes): Promise<EstimateReturn> {
|
||||
return this.estimateWithFee(constants.FOREIGN_TO_HOME_FEE_TYPE, value)
|
||||
}
|
||||
|
||||
async estimateWithFee(feeType: string, value: ValueTypes): Promise<EstimateReturn> {
|
||||
const web3 = this.getExchange()
|
||||
.getAsset(this.assetA)
|
||||
.getWeb3()
|
||||
|
||||
const userAmount = this._getValue(value)
|
||||
|
||||
const contract = new web3.eth.Contract(MEDIATOR_ERC_TO_NATIVE_ABI, this.assetABridge)
|
||||
const { feeAmount, feePercentage } = await this.getFee(feeType, 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(feeType, contract, value): Promise<{ feeAmount: BN; feePercentage: number }> {
|
||||
const fee = toBN(await contract.methods.getFee(feeType).call())
|
||||
const feePercentage = Number(fromWei(fee, 'ether')) * 100
|
||||
const feeAmount = toBN(await contract.methods.calculateFee(feeType, value).call())
|
||||
return {
|
||||
feeAmount,
|
||||
feePercentage
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,12 @@
|
||||
export { ERC677Asset, NativeMediatorAsset, sPOA, Etc, Wetc, TokenBridgeGateway, Mediator } from './burner-wallet'
|
||||
export {
|
||||
ERC677Asset,
|
||||
BridgeableERC20Asset,
|
||||
NativeMediatorAsset,
|
||||
sPOA,
|
||||
Etc,
|
||||
Wetc,
|
||||
TokenBridgeGateway,
|
||||
Mediator,
|
||||
MediatorErcToNative
|
||||
} from './burner-wallet'
|
||||
export { WETCBridge } from './wetc-bridge'
|
||||
|
||||
@@ -3,3 +3,4 @@ export { default as FOREIGN_NATIVE_TO_ERC_ABI } from './abis/ForeignBridgeNative
|
||||
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'
|
||||
export { default as MEDIATOR_ERC_TO_NATIVE_ABI } from './abis/MediatorErcToNative'
|
||||
|
||||
@@ -34,5 +34,23 @@ export default [
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: false,
|
||||
inputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'address'
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'relayTokens',
|
||||
outputs: [],
|
||||
payable: false,
|
||||
stateMutability: 'nonpayable',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
export default [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
}
|
||||
],
|
||||
name: 'getFee',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
constant: true,
|
||||
inputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'bytes32'
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
name: 'calculateFee',
|
||||
outputs: [
|
||||
{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}
|
||||
],
|
||||
payable: false,
|
||||
stateMutability: 'view',
|
||||
type: 'function'
|
||||
}
|
||||
]
|
||||
@@ -4,5 +4,6 @@ export {
|
||||
FOREIGN_NATIVE_TO_ERC_ABI,
|
||||
HOME_NATIVE_TO_ERC_ABI,
|
||||
MEDIATOR_ABI,
|
||||
MEDIATOR_FEE_MANAGER_ABI
|
||||
MEDIATOR_FEE_MANAGER_ABI,
|
||||
MEDIATOR_ERC_TO_NATIVE_ABI
|
||||
} from './abis'
|
||||
|
||||
@@ -7,7 +7,11 @@ export const constants = {
|
||||
EXCHANGE_TIMEOUT: 300000,
|
||||
MAX_FEE: toWei('1', 'ether'),
|
||||
ESTIMATE_FEE_MESSAGE: 'Estimation takes fee charges into consideration.',
|
||||
ZERO_ADDRESS: '0x0000000000000000000000000000000000000000'
|
||||
ZERO_ADDRESS: '0x0000000000000000000000000000000000000000',
|
||||
TRANSFER_TOPIC: '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
|
||||
// fee types are taken from contracts/upgradeable_contracts/amb_erc20_to_native/HomeFeeManagerAMBErc20ToNative.sol
|
||||
HOME_TO_FOREIGN_FEE_TYPE: '0x741ede137d0537e88e0ea0ff25b1f22d837903dbbee8980b4a06e8523247ee26',
|
||||
FOREIGN_TO_HOME_FEE_TYPE: '0x03be2b2875cb41e0e77355e802a16769bb8dfcf825061cde185c73bf94f12625'
|
||||
}
|
||||
|
||||
export const waitForEvent = async (web3, contract: Contract, event: string, callback: Function) => {
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
"lib": [
|
||||
"es6",
|
||||
"dom"
|
||||
]
|
||||
],
|
||||
"types" : [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
||||
@@ -2600,11 +2600,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
|
||||
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
|
||||
|
||||
"@types/mocha@^7.0.2":
|
||||
version "7.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce"
|
||||
integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==
|
||||
|
||||
"@types/node@*", "@types/node@>= 8":
|
||||
version "13.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b"
|
||||
@@ -3220,11 +3215,6 @@ are-we-there-yet@~1.1.2:
|
||||
delegates "^1.0.0"
|
||||
readable-stream "^2.0.6"
|
||||
|
||||
arg@^4.1.0:
|
||||
version "4.1.3"
|
||||
resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
|
||||
integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
|
||||
|
||||
argparse@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
|
||||
@@ -3350,11 +3340,6 @@ assert@^1.1.1:
|
||||
object-assign "^4.1.1"
|
||||
util "0.10.3"
|
||||
|
||||
assertion-error@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b"
|
||||
integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==
|
||||
|
||||
assign-symbols@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
|
||||
@@ -3450,13 +3435,6 @@ axios@^0.18.0:
|
||||
follow-redirects "1.5.10"
|
||||
is-buffer "^2.0.2"
|
||||
|
||||
axios@^0.19.0:
|
||||
version "0.19.2"
|
||||
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27"
|
||||
integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==
|
||||
dependencies:
|
||||
follow-redirects "1.5.10"
|
||||
|
||||
axobject-query@^2.0.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.1.2.tgz#2bdffc0371e643e5f03ba99065d5179b9ca79799"
|
||||
@@ -3821,11 +3799,6 @@ browser-resolve@^1.11.3:
|
||||
dependencies:
|
||||
resolve "1.1.7"
|
||||
|
||||
browser-stdout@1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
|
||||
integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
|
||||
|
||||
browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
|
||||
@@ -3838,7 +3811,7 @@ browserify-aes@^1.0.0, browserify-aes@^1.0.4, browserify-aes@^1.0.6:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
browserify-cipher@^1.0.0, browserify-cipher@^1.0.1:
|
||||
browserify-cipher@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0"
|
||||
integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==
|
||||
@@ -4215,18 +4188,6 @@ caseless@~0.12.0:
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
|
||||
|
||||
chai@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5"
|
||||
integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==
|
||||
dependencies:
|
||||
assertion-error "^1.1.0"
|
||||
check-error "^1.0.2"
|
||||
deep-eql "^3.0.1"
|
||||
get-func-name "^2.0.0"
|
||||
pathval "^1.1.0"
|
||||
type-detect "^4.0.5"
|
||||
|
||||
chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
@@ -4252,11 +4213,6 @@ chardet@^0.7.0:
|
||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||
|
||||
check-error@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
|
||||
integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
|
||||
|
||||
check-types@^8.0.3:
|
||||
version "8.0.3"
|
||||
resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
|
||||
@@ -4490,11 +4446,6 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
|
||||
dependencies:
|
||||
delayed-stream "~1.0.0"
|
||||
|
||||
commander@2.15.1:
|
||||
version "2.15.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
|
||||
integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==
|
||||
|
||||
commander@2.17.x:
|
||||
version "2.17.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf"
|
||||
@@ -5290,13 +5241,6 @@ dedent@^0.7.0:
|
||||
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
|
||||
integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
|
||||
|
||||
deep-eql@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
|
||||
integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==
|
||||
dependencies:
|
||||
type-detect "^4.0.0"
|
||||
|
||||
deep-equal@^1.0.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
|
||||
@@ -5469,16 +5413,6 @@ diff-sequences@^24.9.0:
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
|
||||
integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==
|
||||
|
||||
diff@3.5.0:
|
||||
version "3.5.0"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
|
||||
integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==
|
||||
|
||||
diff@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
|
||||
integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
|
||||
|
||||
diffie-hellman@^5.0.0:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
|
||||
@@ -6531,7 +6465,7 @@ ethers@4.0.44:
|
||||
uuid "2.0.1"
|
||||
xmlhttprequest "1.8.0"
|
||||
|
||||
ethers@^4.0.27, ethers@~4.0.4:
|
||||
ethers@~4.0.4:
|
||||
version "4.0.46"
|
||||
resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.46.tgz#13cd3ed099487f43ece00194b89a8a8781f71507"
|
||||
integrity sha512-/dPMzzpInhtiip4hKFvsDiJKeRk64IhyA+Po7CtNXneQFSOCYXg8eBFt+jXbxUQyApgWnWOtYxWdfn9+CvvxDA==
|
||||
@@ -6546,7 +6480,7 @@ ethers@^4.0.27, ethers@~4.0.4:
|
||||
uuid "2.0.1"
|
||||
xmlhttprequest "1.8.0"
|
||||
|
||||
ethjs-unit@0.1.6, ethjs-unit@^0.1.6:
|
||||
ethjs-unit@0.1.6:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699"
|
||||
integrity sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=
|
||||
@@ -6562,11 +6496,6 @@ ethjs-util@0.1.6, ethjs-util@^0.1.3:
|
||||
is-hex-prefixed "1.0.0"
|
||||
strip-hex-prefix "1.0.0"
|
||||
|
||||
eventemitter3@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
|
||||
integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==
|
||||
|
||||
eventemitter3@3.1.2, eventemitter3@^3.1.0:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
|
||||
@@ -7164,11 +7093,6 @@ get-caller-file@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-func-name@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41"
|
||||
integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=
|
||||
|
||||
get-own-enumerable-property-symbols@^3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
|
||||
@@ -7303,18 +7227,6 @@ glob-to-regexp@^0.3.0:
|
||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
|
||||
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
|
||||
|
||||
glob@7.1.2:
|
||||
version "7.1.2"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
|
||||
integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==
|
||||
dependencies:
|
||||
fs.realpath "^1.0.0"
|
||||
inflight "^1.0.4"
|
||||
inherits "2"
|
||||
minimatch "^3.0.4"
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
@@ -7448,11 +7360,6 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.
|
||||
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
|
||||
integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=
|
||||
|
||||
growl@1.10.5:
|
||||
version "1.10.5"
|
||||
resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e"
|
||||
integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==
|
||||
|
||||
growly@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
|
||||
@@ -7611,11 +7518,6 @@ hdkey@^1.1.1:
|
||||
safe-buffer "^5.1.1"
|
||||
secp256k1 "^3.0.1"
|
||||
|
||||
he@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
||||
integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0=
|
||||
|
||||
he@1.2.x:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
@@ -9690,11 +9592,6 @@ make-dir@^2.0.0, make-dir@^2.1.0:
|
||||
pify "^4.0.1"
|
||||
semver "^5.6.0"
|
||||
|
||||
make-error@^1.1.1:
|
||||
version "1.3.6"
|
||||
resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
|
||||
integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
|
||||
|
||||
make-fetch-happen@^5.0.0:
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd"
|
||||
@@ -10046,11 +9943,6 @@ minimist-options@^3.0.1:
|
||||
arrify "^1.0.1"
|
||||
is-plain-obj "^1.1.0"
|
||||
|
||||
minimist@0.0.8:
|
||||
version "0.0.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||
|
||||
minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
|
||||
version "1.2.5"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||
@@ -10115,13 +10007,6 @@ mkdirp@*:
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
|
||||
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
|
||||
|
||||
mkdirp@0.5.1:
|
||||
version "0.5.1"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
|
||||
dependencies:
|
||||
minimist "0.0.8"
|
||||
|
||||
mkdirp@^0.5.0, mkdirp@^0.5.1:
|
||||
version "0.5.5"
|
||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||
@@ -10136,23 +10021,6 @@ mkdirp@~0.5.0, mkdirp@~0.5.1:
|
||||
dependencies:
|
||||
minimist "^1.2.5"
|
||||
|
||||
mocha@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6"
|
||||
integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==
|
||||
dependencies:
|
||||
browser-stdout "1.3.1"
|
||||
commander "2.15.1"
|
||||
debug "3.1.0"
|
||||
diff "3.5.0"
|
||||
escape-string-regexp "1.0.5"
|
||||
glob "7.1.2"
|
||||
growl "1.10.5"
|
||||
he "1.1.1"
|
||||
minimatch "3.0.4"
|
||||
mkdirp "0.5.1"
|
||||
supports-color "5.4.0"
|
||||
|
||||
mock-fs@^4.1.0:
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.11.0.tgz#0828107e4b843a6ba855ecebfe3c6e073b69db92"
|
||||
@@ -10237,7 +10105,7 @@ nan@2.13.2:
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7"
|
||||
integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==
|
||||
|
||||
nan@2.14.0, nan@^2.0.8, nan@^2.12.1, nan@^2.14.0, nan@^2.2.1:
|
||||
nan@2.14.0, nan@^2.12.1, nan@^2.14.0, nan@^2.2.1:
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
||||
@@ -11099,12 +10967,7 @@ path-type@^4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
|
||||
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
|
||||
|
||||
pathval@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0"
|
||||
integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA=
|
||||
|
||||
pbkdf2@^3.0.17, pbkdf2@^3.0.3:
|
||||
pbkdf2@^3.0.3:
|
||||
version "3.0.17"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
|
||||
integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==
|
||||
@@ -12127,7 +11990,7 @@ querystring@0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||
integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=
|
||||
|
||||
querystringify@^2.0.0, querystringify@^2.1.1:
|
||||
querystringify@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
|
||||
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
||||
@@ -12970,29 +12833,6 @@ scrypt-js@2.0.4:
|
||||
resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.4.tgz#32f8c5149f0797672e551c07e230f834b6af5f16"
|
||||
integrity sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==
|
||||
|
||||
scrypt.js@0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.3.0.tgz#6c62d61728ad533c8c376a2e5e3e86d41a95c4c0"
|
||||
integrity sha512-42LTc1nyFsyv/o0gcHtDztrn+aqpkaCNt5Qh7ATBZfhEZU7IC/0oT/qbBH+uRNoAPvs2fwiOId68FDEoSRA8/A==
|
||||
dependencies:
|
||||
scryptsy "^1.2.1"
|
||||
optionalDependencies:
|
||||
scrypt "^6.0.2"
|
||||
|
||||
scrypt@^6.0.2:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/scrypt/-/scrypt-6.0.3.tgz#04e014a5682b53fa50c2d5cce167d719c06d870d"
|
||||
integrity sha1-BOAUpWgrU/pQwtXM4WfXGcBthw0=
|
||||
dependencies:
|
||||
nan "^2.0.8"
|
||||
|
||||
scryptsy@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-1.2.1.tgz#a3225fa4b2524f802700761e2855bdf3b2d92163"
|
||||
integrity sha1-oyJfpLJST4AnAHYeKFW987LZIWM=
|
||||
dependencies:
|
||||
pbkdf2 "^3.0.3"
|
||||
|
||||
scryptsy@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790"
|
||||
@@ -13872,13 +13712,6 @@ stylis@^3.5.0:
|
||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe"
|
||||
integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==
|
||||
|
||||
supports-color@5.4.0:
|
||||
version "5.4.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
|
||||
integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
supports-color@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
|
||||
@@ -14243,17 +14076,6 @@ tryer@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
|
||||
integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA==
|
||||
|
||||
ts-node@^8.8.2:
|
||||
version "8.8.2"
|
||||
resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.8.2.tgz#0b39e690bee39ea5111513a9d2bcdc0bc121755f"
|
||||
integrity sha512-duVj6BpSpUpD/oM4MfhO98ozgkp3Gt9qIp3jGxwU2DFvl/3IRaEAvbLa8G60uS7C77457e/m5TMowjedeRxI1Q==
|
||||
dependencies:
|
||||
arg "^4.1.0"
|
||||
diff "^4.0.1"
|
||||
make-error "^1.1.1"
|
||||
source-map-support "^0.5.6"
|
||||
yn "3.1.1"
|
||||
|
||||
ts-pnp@1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.2.tgz#be8e4bfce5d00f0f58e0666a82260c34a57af552"
|
||||
@@ -14300,11 +14122,6 @@ type-check@~0.3.2:
|
||||
dependencies:
|
||||
prelude-ls "~1.1.2"
|
||||
|
||||
type-detect@^4.0.0, type-detect@^4.0.5:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||
|
||||
type-fest@^0.3.0:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
|
||||
@@ -14350,11 +14167,6 @@ typescript@3.5.3:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
|
||||
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
|
||||
|
||||
typescript@^3.5.2:
|
||||
version "3.8.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
|
||||
integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==
|
||||
|
||||
uglify-js@3.4.x:
|
||||
version "3.4.10"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
|
||||
@@ -14538,14 +14350,6 @@ url-parse-lax@^3.0.0:
|
||||
dependencies:
|
||||
prepend-http "^2.0.0"
|
||||
|
||||
url-parse@1.4.4:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.4.tgz#cac1556e95faa0303691fec5cf9d5a1bc34648f8"
|
||||
integrity sha512-/92DTTorg4JjktLNLe6GPS2/RvAd/RGr6LuktmWSMLEOa6rjnlrFXNgSbSmkNvCoL2T028A0a1JaJLzRMlFoHg==
|
||||
dependencies:
|
||||
querystringify "^2.0.0"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
url-parse@^1.4.3:
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
|
||||
@@ -14577,11 +14381,6 @@ use@^3.1.0:
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==
|
||||
|
||||
utf8@2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/utf8/-/utf8-2.1.1.tgz#2e01db02f7d8d0944f77104f1609eb0c304cf768"
|
||||
integrity sha1-LgHbAvfY0JRPdxBPFgnrDDBM92g=
|
||||
|
||||
utf8@3.0.0, utf8@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/utf8/-/utf8-3.0.0.tgz#f052eed1364d696e769ef058b183df88c87f69d1"
|
||||
@@ -14771,17 +14570,6 @@ web3-bzz@1.2.6:
|
||||
swarm-js "0.1.39"
|
||||
underscore "1.9.1"
|
||||
|
||||
web3-core-helpers@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.0.0-beta.55.tgz#832b8499889f9f514b1d174f00172fd3683d63de"
|
||||
integrity sha512-suj9Xy/lIqajaYLJTEjr2rlFgu6hGYwChHmf8+qNrC2luZA6kirTamtB9VThWMxbywx7p0bqQFjW6zXogAgWhg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
lodash "^4.17.11"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-eth-iban "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-core-helpers@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.2.6.tgz#7aacd25bf8015adcdfc0f3243d0dcfdff0373f7d"
|
||||
@@ -14791,20 +14579,6 @@ web3-core-helpers@1.2.6:
|
||||
web3-eth-iban "1.2.6"
|
||||
web3-utils "1.2.6"
|
||||
|
||||
web3-core-method@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.0.0-beta.55.tgz#0af994295ac2dd64ccd53305b7df8da76e11da49"
|
||||
integrity sha512-w1cW/s2ji9qGELHk2uMJCn1ooay0JJLVoPD1nvmsW6OTRWcVjxa62nJrFQhe6P5lEb83Xk9oHgmCxZoVUHibOw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
eventemitter3 "3.1.0"
|
||||
lodash "^4.17.11"
|
||||
rxjs "^6.4.0"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-core-helpers "1.0.0-beta.55"
|
||||
web3-core-subscriptions "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-core-method@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.2.6.tgz#f5a3e4d304abaf382923c8ab88ec8eeef45c1b3b"
|
||||
@@ -14835,15 +14609,6 @@ web3-core-requestmanager@1.2.6:
|
||||
web3-providers-ipc "1.2.6"
|
||||
web3-providers-ws "1.2.6"
|
||||
|
||||
web3-core-subscriptions@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.0.0-beta.55.tgz#105902c13db53466fc17d07a981ad3d41c700f76"
|
||||
integrity sha512-pb3oQbUzK7IoyXwag8TYInQddg0rr7BHxKc+Pbs/92hVNQ5ps4iGMVJKezdrjlQ1IJEEUiDIglXl4LZ1hIuMkw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
eventemitter3 "^3.1.0"
|
||||
lodash "^4.17.11"
|
||||
|
||||
web3-core-subscriptions@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.2.6.tgz#9d44189e2321f8f1abc31f6c09103b5283461b57"
|
||||
@@ -14853,19 +14618,6 @@ web3-core-subscriptions@1.2.6:
|
||||
underscore "1.9.1"
|
||||
web3-core-helpers "1.2.6"
|
||||
|
||||
web3-core@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.0.0-beta.55.tgz#26b9abbf1bc1837c9cc90f06ecbc4ed714f89b53"
|
||||
integrity sha512-AMMp7TLEtE7u8IJAu/THrRhBTZyZzeo7Y6GiWYNwb5+KStC9hIGLr9cI1KX9R6ZioTOLRHrqT7awDhnJ1ku2mg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
"@types/bn.js" "^4.11.4"
|
||||
"@types/node" "^10.12.18"
|
||||
lodash "^4.17.11"
|
||||
web3-core-method "1.0.0-beta.55"
|
||||
web3-providers "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-core@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.2.6.tgz#bb42a1d7ae49a7258460f0d95ddb00906f59ef92"
|
||||
@@ -14878,16 +14630,6 @@ web3-core@1.2.6:
|
||||
web3-core-requestmanager "1.2.6"
|
||||
web3-utils "1.2.6"
|
||||
|
||||
web3-eth-abi@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.0.0-beta.55.tgz#69250420039346105a3d0f899c0a8a53be926f97"
|
||||
integrity sha512-3h1xnm/vYmKUXTOYAOP0OsB5uijQV76pNNRGKOB6Dq6GR1pbcbD3WrB/4I643YA8l91t5FRzFzUiA3S77R2iqw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
ethers "^4.0.27"
|
||||
lodash "^4.17.11"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-eth-abi@1.2.6, web3-eth-abi@^1.2.1:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.6.tgz#b495383cc5c0d8e2857b26e7fe25606685983b25"
|
||||
@@ -14897,25 +14639,6 @@ web3-eth-abi@1.2.6, web3-eth-abi@^1.2.1:
|
||||
underscore "1.9.1"
|
||||
web3-utils "1.2.6"
|
||||
|
||||
web3-eth-accounts@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.55.tgz#ba734ffdc1e3cc8ac0ea01de5241323a0c2f69f3"
|
||||
integrity sha512-VfzvwpSDHXqRVelIxsBVhgbV9BkFvhJ/q+bKhnVUUXV0JAhMK/7uC92TsqKk4EBYuqpHyZ1jjqrL4n03fMU7zw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
browserify-cipher "^1.0.1"
|
||||
eth-lib "0.2.8"
|
||||
lodash "^4.17.11"
|
||||
pbkdf2 "^3.0.17"
|
||||
randombytes "^2.1.0"
|
||||
scrypt.js "0.3.0"
|
||||
uuid "3.3.2"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-core-helpers "1.0.0-beta.55"
|
||||
web3-core-method "1.0.0-beta.55"
|
||||
web3-providers "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-eth-accounts@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.6.tgz#a1ba4bf75fa8102a3ec6cddd0eccd72462262720"
|
||||
@@ -14934,23 +14657,6 @@ web3-eth-accounts@1.2.6:
|
||||
web3-core-method "1.2.6"
|
||||
web3-utils "1.2.6"
|
||||
|
||||
web3-eth-contract@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.55.tgz#cd9e6727ff73d648ebe7cae17516e8aec5873c65"
|
||||
integrity sha512-v6oB1wfH039/A5sTb4ZTKX++fcBTHEkuQGpq50ATIDoxP/UTz2+6S+iL+3sCJTsByPw2/Bni/HM7NmLkXqzg/Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
"@types/bn.js" "^4.11.4"
|
||||
lodash "^4.17.11"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-core-helpers "1.0.0-beta.55"
|
||||
web3-core-method "1.0.0-beta.55"
|
||||
web3-core-subscriptions "1.0.0-beta.55"
|
||||
web3-eth-abi "1.0.0-beta.55"
|
||||
web3-eth-accounts "1.0.0-beta.55"
|
||||
web3-providers "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-eth-contract@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.2.6.tgz#39111543960035ed94c597a239cf5aa1da796741"
|
||||
@@ -14966,24 +14672,6 @@ web3-eth-contract@1.2.6:
|
||||
web3-eth-abi "1.2.6"
|
||||
web3-utils "1.2.6"
|
||||
|
||||
web3-eth-ens@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.0.0-beta.55.tgz#4341434a3406728212d411ae7f22d4cf5b8642fe"
|
||||
integrity sha512-jEL17coO0FJXb7KYq4+7DhVXj0Rh+wHfZ86jOvFUvJsRaUHfqK2TlMatuhD2mbrmxpBYb6oMPnXVnNK9bnD5Rg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
eth-ens-namehash "2.0.8"
|
||||
lodash "^4.17.11"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-core-helpers "1.0.0-beta.55"
|
||||
web3-core-method "1.0.0-beta.55"
|
||||
web3-eth-abi "1.0.0-beta.55"
|
||||
web3-eth-accounts "1.0.0-beta.55"
|
||||
web3-eth-contract "1.0.0-beta.55"
|
||||
web3-net "1.0.0-beta.55"
|
||||
web3-providers "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-eth-ens@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.2.6.tgz#bf86a624c4c72bc59913c2345180d3ea947e110d"
|
||||
@@ -14998,15 +14686,6 @@ web3-eth-ens@1.2.6:
|
||||
web3-eth-contract "1.2.6"
|
||||
web3-utils "1.2.6"
|
||||
|
||||
web3-eth-iban@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.0.0-beta.55.tgz#15146a69de21addc99e7dbfb2920555b1e729637"
|
||||
integrity sha512-a2Fxsb5Mssa+jiXgjUdIzJipE0175IcQXJbZLpKft2+zeSJWNTbaa3PQD2vPPpIM4W789q06N+f9Zc0Fyls+1g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
bn.js "4.11.8"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-eth-iban@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.2.6.tgz#0b22191fd1aa6e27f7ef0820df75820bfb4ed46b"
|
||||
@@ -15015,20 +14694,6 @@ web3-eth-iban@1.2.6:
|
||||
bn.js "4.11.8"
|
||||
web3-utils "1.2.6"
|
||||
|
||||
web3-eth-personal@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.0.0-beta.55.tgz#76e9d2da1501ee3c686751e7c7df63cc11793a1d"
|
||||
integrity sha512-H0mahLQx6Oj7lpgTamKAswr3rHChRUZijeWAar2Hj7BABQlLRKwx8n09nYhxggvvLYQNQS90JjvQue7rAo2LQQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-core-helpers "1.0.0-beta.55"
|
||||
web3-core-method "1.0.0-beta.55"
|
||||
web3-eth-accounts "1.0.0-beta.55"
|
||||
web3-net "1.0.0-beta.55"
|
||||
web3-providers "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-eth-personal@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.2.6.tgz#47a0a0657ec04dd77f95451a6869d4751d324b6b"
|
||||
@@ -15041,28 +14706,6 @@ web3-eth-personal@1.2.6:
|
||||
web3-net "1.2.6"
|
||||
web3-utils "1.2.6"
|
||||
|
||||
web3-eth@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.0.0-beta.55.tgz#bb52150df0a77bd13511449a53793d4eb23ade6e"
|
||||
integrity sha512-F3zJ9I1gOgQdNGfi2Dy2lmj6OqCMJoRN01XHhQZagq0HY1JYMfObtfMi5E3L+qsegsSddHbqp4YY57tKx6uxpA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
ethereumjs-tx "^1.3.7"
|
||||
rxjs "^6.4.0"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-core-helpers "1.0.0-beta.55"
|
||||
web3-core-method "1.0.0-beta.55"
|
||||
web3-core-subscriptions "1.0.0-beta.55"
|
||||
web3-eth-abi "1.0.0-beta.55"
|
||||
web3-eth-accounts "1.0.0-beta.55"
|
||||
web3-eth-contract "1.0.0-beta.55"
|
||||
web3-eth-ens "1.0.0-beta.55"
|
||||
web3-eth-iban "1.0.0-beta.55"
|
||||
web3-eth-personal "1.0.0-beta.55"
|
||||
web3-net "1.0.0-beta.55"
|
||||
web3-providers "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-eth@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.2.6.tgz#15a8c65fdde0727872848cae506758d302d8d046"
|
||||
@@ -15082,19 +14725,6 @@ web3-eth@1.2.6:
|
||||
web3-net "1.2.6"
|
||||
web3-utils "1.2.6"
|
||||
|
||||
web3-net@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.0.0-beta.55.tgz#daf24323df16a890a0bac6c6eda48b6e8c7e96ef"
|
||||
integrity sha512-do2WY8+/GArJSWX7k/zZ7nBnV9Y3n6LhPYkwT3LeFqDzD515bKwlomaNC8hOaTc6UQyXIoPprYTK2FevL7jrZw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
lodash "^4.17.11"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-core-helpers "1.0.0-beta.55"
|
||||
web3-core-method "1.0.0-beta.55"
|
||||
web3-providers "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-net@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.2.6.tgz#035ca0fbe55282fda848ca17ebb4c8966147e5ea"
|
||||
@@ -15158,37 +14788,6 @@ web3-providers-ws@1.2.6:
|
||||
underscore "1.9.1"
|
||||
web3-core-helpers "1.2.6"
|
||||
|
||||
web3-providers@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-providers/-/web3-providers-1.0.0-beta.55.tgz#639503517741b69baaa82f1f940630df6a25992b"
|
||||
integrity sha512-MNifc7W+iF6rykpbDR1MuX152jshWdZXHAU9Dk0Ja2/23elhIs4nCWs7wOX9FHrKgdrQbscPoq0uy+0aGzyWVQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
"@types/node" "^10.12.18"
|
||||
eventemitter3 "3.1.0"
|
||||
lodash "^4.17.11"
|
||||
url-parse "1.4.4"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-core-helpers "1.0.0-beta.55"
|
||||
web3-core-method "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
websocket "^1.0.28"
|
||||
xhr2-cookies "1.1.0"
|
||||
|
||||
web3-shh@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.0.0-beta.55.tgz#56f152ebcefb791dab86d2e6f1c296f8c1553644"
|
||||
integrity sha512-lGP2HQ/1ThNnfoU8677aL48KsTx4Ht+2KQIn39dGpxVZqysQmovQIltbymVnAr4h8wofwcEz46iNHGa+PAyNzA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-core-helpers "1.0.0-beta.55"
|
||||
web3-core-method "1.0.0-beta.55"
|
||||
web3-core-subscriptions "1.0.0-beta.55"
|
||||
web3-net "1.0.0-beta.55"
|
||||
web3-providers "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3-shh@1.2.6:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.2.6.tgz#2492616da4cac32d4c7534b890f43bac63190c14"
|
||||
@@ -15199,22 +14798,6 @@ web3-shh@1.2.6:
|
||||
web3-core-subscriptions "1.2.6"
|
||||
web3-net "1.2.6"
|
||||
|
||||
web3-utils@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.0.0-beta.55.tgz#beb40926b7c04208b752d36a9bc959d27a04b308"
|
||||
integrity sha512-ASWqUi8gtWK02Tp8ZtcoAbHenMpQXNvHrakgzvqTNNZn26wgpv+Q4mdPi0KOR6ZgHFL8R/9b5BBoUTglS1WPpg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
"@types/bn.js" "^4.11.4"
|
||||
"@types/node" "^10.12.18"
|
||||
bn.js "4.11.8"
|
||||
eth-lib "0.2.8"
|
||||
ethjs-unit "^0.1.6"
|
||||
lodash "^4.17.11"
|
||||
number-to-bn "1.7.0"
|
||||
randombytes "^2.1.0"
|
||||
utf8 "2.1.1"
|
||||
|
||||
web3-utils@1.2.6, web3-utils@^1.2.1, web3-utils@^1.2.2:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.6.tgz#b9a25432da00976457fcc1094c4af8ac6d486db9"
|
||||
@@ -15229,21 +14812,6 @@ web3-utils@1.2.6, web3-utils@^1.2.1, web3-utils@^1.2.2:
|
||||
underscore "1.9.1"
|
||||
utf8 "3.0.0"
|
||||
|
||||
web3@1.0.0-beta.55:
|
||||
version "1.0.0-beta.55"
|
||||
resolved "https://registry.yarnpkg.com/web3/-/web3-1.0.0-beta.55.tgz#8845075129299da172c2eb41a748c8a87c2a2b5a"
|
||||
integrity sha512-yJpwy4IUA3T/F9hWzYQVn0GbJCrAaZ0KTIO3iuqkhaYH0Y09KV7k4GzFi4hN7hT4cFTj4yIKaeVCwQ5kzvi2Vg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
"@types/node" "^10.12.18"
|
||||
web3-core "1.0.0-beta.55"
|
||||
web3-eth "1.0.0-beta.55"
|
||||
web3-eth-personal "1.0.0-beta.55"
|
||||
web3-net "1.0.0-beta.55"
|
||||
web3-providers "1.0.0-beta.55"
|
||||
web3-shh "1.0.0-beta.55"
|
||||
web3-utils "1.0.0-beta.55"
|
||||
|
||||
web3@^1.2.1, web3@^1.2.2:
|
||||
version "1.2.6"
|
||||
resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.6.tgz#c497dcb14cdd8d6d9fb6b445b3b68ff83f8ccf68"
|
||||
@@ -15406,17 +14974,6 @@ websocket-extensions@>=0.1.1:
|
||||
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
|
||||
integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
|
||||
|
||||
websocket@^1.0.28:
|
||||
version "1.0.31"
|
||||
resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.31.tgz#e5d0f16c3340ed87670e489ecae6144c79358730"
|
||||
integrity sha512-VAouplvGKPiKFDTeCCO65vYHsyay8DqoBSlzIO3fayrfOgU94lQN5a1uWVnFrMLceTJw/+fQXR5PGbUVRaHshQ==
|
||||
dependencies:
|
||||
debug "^2.2.0"
|
||||
es5-ext "^0.10.50"
|
||||
nan "^2.14.0"
|
||||
typedarray-to-buffer "^3.1.5"
|
||||
yaeti "^0.0.6"
|
||||
|
||||
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
|
||||
@@ -15924,8 +15481,3 @@ yauzl@^2.4.2:
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
fd-slicer "~1.1.0"
|
||||
|
||||
yn@3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
|
||||
integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
|
||||
|
||||
@@ -7,13 +7,17 @@ const FOREIGN_ERC_TO_NATIVE_ABI = require('../contracts/build/contracts/ForeignB
|
||||
const ERC20_ABI = require('../contracts/build/contracts/ERC20').abi
|
||||
const ERC677_ABI = require('../contracts/build/contracts/ERC677').abi
|
||||
const ERC677_BRIDGE_TOKEN_ABI = require('../contracts/build/contracts/ERC677BridgeToken').abi
|
||||
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/IBlockReward').abi
|
||||
const BLOCK_REWARD_ABI = require('../contracts/build/contracts/BlockReward').abi
|
||||
const BRIDGE_VALIDATORS_ABI = require('../contracts/build/contracts/BridgeValidators').abi
|
||||
const REWARDABLE_VALIDATORS_ABI = require('../contracts/build/contracts/RewardableValidators').abi
|
||||
const HOME_AMB_ABI = require('../contracts/build/contracts/HomeAMB').abi
|
||||
const FOREIGN_AMB_ABI = require('../contracts/build/contracts/ForeignAMB').abi
|
||||
const BOX_ABI = require('../contracts/build/contracts/Box').abi
|
||||
const SAI_TOP = require('../contracts/build/contracts/SaiTopMock').abi
|
||||
const HOME_AMB_ERC_TO_ERC_ABI = require('../contracts/build/contracts/HomeAMBErc677ToErc677').abi
|
||||
const FOREIGN_AMB_ERC_TO_ERC_ABI = require('../contracts/build/contracts/ForeignAMBErc677ToErc677').abi
|
||||
const HOME_STAKE_ERC_TO_ERC_ABI = require('../contracts/build/contracts/HomeStakeTokenMediator').abi
|
||||
const FOREIGN_STAKE_ERC_TO_ERC_ABI = require('../contracts/build/contracts/ForeignStakeTokenMediator').abi
|
||||
|
||||
const { HOME_V1_ABI, FOREIGN_V1_ABI } = require('./v1Abis')
|
||||
const { BRIDGE_MODES } = require('./constants')
|
||||
@@ -49,6 +53,36 @@ const ERC20_BYTES32_ABI = [
|
||||
}
|
||||
]
|
||||
|
||||
const OLD_AMB_USER_REQUEST_FOR_SIGNATURE_ABI = [
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'encodedData',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'UserRequestForSignature',
|
||||
type: 'event'
|
||||
}
|
||||
]
|
||||
|
||||
const OLD_AMB_USER_REQUEST_FOR_AFFIRMATION_ABI = [
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [
|
||||
{
|
||||
indexed: false,
|
||||
name: 'encodedData',
|
||||
type: 'bytes'
|
||||
}
|
||||
],
|
||||
name: 'UserRequestForAffirmation',
|
||||
type: 'event'
|
||||
}
|
||||
]
|
||||
|
||||
function getBridgeABIs(bridgeMode) {
|
||||
let HOME_ABI = null
|
||||
let FOREIGN_ABI = null
|
||||
@@ -67,6 +101,12 @@ function getBridgeABIs(bridgeMode) {
|
||||
} else if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) {
|
||||
HOME_ABI = HOME_AMB_ABI
|
||||
FOREIGN_ABI = FOREIGN_AMB_ABI
|
||||
} else if (bridgeMode === BRIDGE_MODES.AMB_ERC_TO_ERC) {
|
||||
HOME_ABI = HOME_AMB_ERC_TO_ERC_ABI
|
||||
FOREIGN_ABI = FOREIGN_AMB_ERC_TO_ERC_ABI
|
||||
} else if (bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC) {
|
||||
HOME_ABI = HOME_STAKE_ERC_TO_ERC_ABI
|
||||
FOREIGN_ABI = FOREIGN_STAKE_ERC_TO_ERC_ABI
|
||||
} else {
|
||||
throw new Error(`Unrecognized bridge mode: ${bridgeMode}`)
|
||||
}
|
||||
@@ -93,6 +133,10 @@ module.exports = {
|
||||
ERC20_BYTES32_ABI,
|
||||
HOME_AMB_ABI,
|
||||
FOREIGN_AMB_ABI,
|
||||
OLD_AMB_USER_REQUEST_FOR_AFFIRMATION_ABI,
|
||||
OLD_AMB_USER_REQUEST_FOR_SIGNATURE_ABI,
|
||||
BOX_ABI,
|
||||
SAI_TOP
|
||||
SAI_TOP,
|
||||
HOME_STAKE_ERC_TO_ERC_ABI,
|
||||
FOREIGN_STAKE_ERC_TO_ERC_ABI
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ const BRIDGE_MODES = {
|
||||
ERC_TO_ERC: 'ERC_TO_ERC',
|
||||
ERC_TO_NATIVE: 'ERC_TO_NATIVE',
|
||||
NATIVE_TO_ERC_V1: 'NATIVE_TO_ERC_V1',
|
||||
ARBITRARY_MESSAGE: 'ARBITRARY_MESSAGE'
|
||||
ARBITRARY_MESSAGE: 'ARBITRARY_MESSAGE',
|
||||
AMB_ERC_TO_ERC: 'AMB_ERC_TO_ERC',
|
||||
STAKE_AMB_ERC_TO_ERC: 'STAKE_AMB_ERC_TO_ERC'
|
||||
}
|
||||
|
||||
const ERC_TYPES = {
|
||||
@@ -14,6 +16,7 @@ const ERC_TYPES = {
|
||||
const FEE_MANAGER_MODE = {
|
||||
ONE_DIRECTION: 'ONE_DIRECTION',
|
||||
BOTH_DIRECTIONS: 'BOTH_DIRECTIONS',
|
||||
ONE_DIRECTION_STAKE: 'ONE_DIRECTION_STAKE',
|
||||
UNDEFINED: 'UNDEFINED'
|
||||
}
|
||||
|
||||
|
||||
@@ -9,14 +9,14 @@ function addTxHashToData({ encodedData, transactionHash }) {
|
||||
function parseAMBMessage(message) {
|
||||
message = strip0x(message)
|
||||
|
||||
const txHash = `0x${message.slice(0, 64)}`
|
||||
const messageId = `0x${message.slice(0, 64)}`
|
||||
const sender = `0x${message.slice(64, 104)}`
|
||||
const executor = `0x${message.slice(104, 144)}`
|
||||
|
||||
return {
|
||||
sender,
|
||||
executor,
|
||||
txHash
|
||||
messageId
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ const { BRIDGE_MODES, ERC_TYPES } = require('../constants')
|
||||
|
||||
describe('constants', () => {
|
||||
it('should contain correct number of bridge types', () => {
|
||||
expect(Object.keys(BRIDGE_MODES).length).to.be.equal(5)
|
||||
expect(Object.keys(BRIDGE_MODES).length).to.be.equal(7)
|
||||
})
|
||||
|
||||
it('should contain correct number of erc types', () => {
|
||||
|
||||
@@ -61,4 +61,99 @@ describe('getTokenType', () => {
|
||||
// Then
|
||||
expect(type).to.equal(ERC_TYPES.ERC20)
|
||||
})
|
||||
|
||||
it('should return ERC20 if bridgeContract and isBridge are not present', async () => {
|
||||
// Given
|
||||
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
|
||||
const contract = {
|
||||
methods: {
|
||||
bridgeContract: () => {
|
||||
return {
|
||||
call: () => Promise.reject()
|
||||
}
|
||||
},
|
||||
isBridge: () => {
|
||||
return {
|
||||
call: () => Promise.reject()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
const type = await getTokenType(contract, bridgeAddress)
|
||||
|
||||
// Then
|
||||
expect(type).to.equal(ERC_TYPES.ERC20)
|
||||
})
|
||||
|
||||
it('should return ERC677 if isBridge returns true', async () => {
|
||||
// Given
|
||||
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
|
||||
const contract = {
|
||||
methods: {
|
||||
bridgeContract: () => {
|
||||
return {
|
||||
call: () => Promise.reject()
|
||||
}
|
||||
},
|
||||
isBridge: () => {
|
||||
return {
|
||||
call: () => Promise.resolve(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
const type = await getTokenType(contract, bridgeAddress)
|
||||
|
||||
// Then
|
||||
expect(type).to.equal(ERC_TYPES.ERC677)
|
||||
})
|
||||
|
||||
it('should return ERC677 if isBridge returns true and bridgeContract not present', async () => {
|
||||
// Given
|
||||
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
|
||||
const contract = {
|
||||
methods: {
|
||||
isBridge: () => {
|
||||
return {
|
||||
call: () => Promise.resolve(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
const type = await getTokenType(contract, bridgeAddress)
|
||||
|
||||
// Then
|
||||
expect(type).to.equal(ERC_TYPES.ERC677)
|
||||
})
|
||||
|
||||
it('should return ERC20 if isBridge returns false', async () => {
|
||||
// Given
|
||||
const bridgeAddress = '0xCecBE80Ed3548dE11D7d2D922a36576eA40C4c26'
|
||||
const contract = {
|
||||
methods: {
|
||||
bridgeContract: () => {
|
||||
return {
|
||||
call: () => Promise.reject()
|
||||
}
|
||||
},
|
||||
isBridge: () => {
|
||||
return {
|
||||
call: () => Promise.resolve(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When
|
||||
const type = await getTokenType(contract, bridgeAddress)
|
||||
|
||||
// Then
|
||||
expect(type).to.equal(ERC_TYPES.ERC20)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -50,20 +50,20 @@ describe('parseAMBMessage', () => {
|
||||
it('should parse data type 00', () => {
|
||||
const msgSender = '0x003667154bb32e42bb9e1e6532f19d187fa0082e'
|
||||
const msgExecutor = '0xf4bef13f9f4f2b203faf0c3cbbaabe1afe056955'
|
||||
const msgTxHash = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10'
|
||||
const msgId = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10'
|
||||
const msgGasLimit = '000000000000000000000000000000000000000000000000000000005b877705'
|
||||
const msgDataType = '00'
|
||||
const msgData = '0xb1591967aed668a4b27645ff40c444892d91bf5951b382995d4d4f6ee3a2ce03'
|
||||
const message = `0x${strip0x(msgTxHash)}${strip0x(msgSender)}${strip0x(
|
||||
const message = `0x${strip0x(msgId)}${strip0x(msgSender)}${strip0x(
|
||||
msgExecutor
|
||||
)}${msgGasLimit}${msgDataType}${strip0x(msgData)}`
|
||||
|
||||
// when
|
||||
const { sender, executor, txHash } = parseAMBMessage(message)
|
||||
const { sender, executor, messageId } = parseAMBMessage(message)
|
||||
|
||||
// then
|
||||
expect(sender).to.be.equal(msgSender)
|
||||
expect(executor).to.be.equal(msgExecutor)
|
||||
expect(txHash).to.be.equal(msgTxHash)
|
||||
expect(messageId).to.be.equal(msgId)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -12,6 +12,10 @@ function decodeBridgeMode(bridgeModeHash) {
|
||||
return BRIDGE_MODES.ERC_TO_NATIVE
|
||||
case '0x2544fbb9':
|
||||
return BRIDGE_MODES.ARBITRARY_MESSAGE
|
||||
case '0x16ea01e9':
|
||||
return BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC
|
||||
case '0x76595b56':
|
||||
return BRIDGE_MODES.AMB_ERC_TO_ERC
|
||||
default:
|
||||
throw new Error(`Unrecognized bridge mode hash: '${bridgeModeHash}'`)
|
||||
}
|
||||
@@ -46,10 +50,31 @@ const getTokenType = async (bridgeTokenContract, bridgeAddress) => {
|
||||
return ERC_TYPES.ERC20
|
||||
}
|
||||
} catch (e) {
|
||||
return ERC_TYPES.ERC20
|
||||
try {
|
||||
const isBridge = await bridgeTokenContract.methods.isBridge(bridgeAddress).call()
|
||||
if (isBridge) {
|
||||
return ERC_TYPES.ERC677
|
||||
} else {
|
||||
return ERC_TYPES.ERC20
|
||||
}
|
||||
} catch (e) {
|
||||
return ERC_TYPES.ERC20
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isErcToErcMode = bridgeMode => {
|
||||
return (
|
||||
bridgeMode === BRIDGE_MODES.ERC_TO_ERC ||
|
||||
bridgeMode === BRIDGE_MODES.AMB_ERC_TO_ERC ||
|
||||
bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC
|
||||
)
|
||||
}
|
||||
|
||||
const isMediatorMode = bridgeMode => {
|
||||
return bridgeMode === BRIDGE_MODES.AMB_ERC_TO_ERC || bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC
|
||||
}
|
||||
|
||||
const getUnit = bridgeMode => {
|
||||
let unitHome = null
|
||||
let unitForeign = null
|
||||
@@ -62,6 +87,9 @@ const getUnit = bridgeMode => {
|
||||
} else if (bridgeMode === BRIDGE_MODES.ERC_TO_NATIVE) {
|
||||
unitHome = 'Native coins'
|
||||
unitForeign = 'Tokens'
|
||||
} else if (bridgeMode === BRIDGE_MODES.STAKE_AMB_ERC_TO_ERC) {
|
||||
unitHome = 'Tokens'
|
||||
unitForeign = 'Tokens'
|
||||
} else {
|
||||
throw new Error(`Unrecognized bridge mode: ${bridgeMode}`)
|
||||
}
|
||||
@@ -260,5 +288,7 @@ module.exports = {
|
||||
normalizeGasPrice,
|
||||
gasPriceFromSupplier,
|
||||
gasPriceFromContract,
|
||||
gasPriceWithinLimits
|
||||
gasPriceWithinLimits,
|
||||
isErcToErcMode,
|
||||
isMediatorMode
|
||||
}
|
||||
|
||||
Submodule contracts updated: a7ce4441ab...d67761d938
@@ -0,0 +1,14 @@
|
||||
# Molecule managed
|
||||
|
||||
{% if item.registry is defined %}
|
||||
FROM {{ item.registry.url }}/{{ item.image }}
|
||||
{% else %}
|
||||
FROM {{ item.image }}
|
||||
{% endif %}
|
||||
|
||||
RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \
|
||||
elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \
|
||||
elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl bash && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \
|
||||
elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \
|
||||
elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \
|
||||
elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
driver:
|
||||
name: docker
|
||||
platforms:
|
||||
- name: oracle-amb-host
|
||||
groups:
|
||||
- ultimate
|
||||
- amb
|
||||
children:
|
||||
- oracle
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
- name: ui-amb-stake-erc-to-erc-host
|
||||
groups:
|
||||
- ultimate
|
||||
- amb-stake-erc-to-erc
|
||||
children:
|
||||
- ui
|
||||
image: ubuntu:16.04
|
||||
privileged: true
|
||||
network_mode: host
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
provisioner:
|
||||
name: ansible
|
||||
playbooks:
|
||||
prepare: ../prepare.yml
|
||||
converge: ../ultimate-commons/converge.yml
|
||||
inventory:
|
||||
host_vars:
|
||||
oracle-amb-host:
|
||||
COMMON_HOME_RPC_URL: "http://parity1:8545"
|
||||
COMMON_FOREIGN_RPC_URL: "http://parity2:8545"
|
||||
ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b"
|
||||
ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9"
|
||||
ui-amb-stake-erc-to-erc-host:
|
||||
COMMON_HOME_RPC_URL: "http://localhost:8541"
|
||||
COMMON_FOREIGN_RPC_URL: "http://localhost:8542"
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
||||
scenario:
|
||||
name: ultimate-amb-stake-erc-to-erc
|
||||
test_sequence:
|
||||
- cleanup
|
||||
- destroy
|
||||
- syntax
|
||||
- create
|
||||
- prepare
|
||||
- converge
|
||||
4
deployment/group_vars/amb-stake-erc-to-erc.yml
Normal file
4
deployment/group_vars/amb-stake-erc-to-erc.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
COMMON_HOME_BRIDGE_ADDRESS: "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC"
|
||||
COMMON_FOREIGN_BRIDGE_ADDRESS: "0xc26Aa60Ff574f157616D3aEE70e08aAC129E1dFC"
|
||||
UI_PORT: 3003
|
||||
@@ -32,6 +32,7 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
## UI
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_OG_TITLE: "POA Bridge UI"
|
||||
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring MakerDAO stable tokens between the Ethereum network to xDai chain in a quick and cost-efficient manner."
|
||||
UI_PORT: 3001
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/dai/tx/%s
|
||||
@@ -40,6 +41,8 @@ UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/dai/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_STYLES: "core"
|
||||
UI_PUBLIC_URL: "https://dai-bridge.poa.network"
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "xdai"
|
||||
|
||||
@@ -34,6 +34,7 @@ ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
## UI
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_OG_TITLE: "POA Bridge UI"
|
||||
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
|
||||
UI_PORT: 3001
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
|
||||
@@ -42,6 +43,8 @@ UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_STYLES: "core"
|
||||
UI_PUBLIC_URL: "http://localhost:3001"
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
|
||||
@@ -33,6 +33,7 @@ ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
|
||||
#ui
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_OG_TITLE: "POA Bridge UI"
|
||||
UI_DESCRIPTION: "The POA cross-chain bridge serves as a method of transferring POA native tokens from the POA Network to the Ethereum network in a quick and cost-efficient manner."
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/poa/sokol/tx/%s
|
||||
UI_FOREIGN_EXPLORER_TX_TEMPLATE: https://blockscout.com/eth/kovan/tx/%s
|
||||
@@ -40,6 +41,8 @@ UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/poa/sokol/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/kovan/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_STYLES: "core"
|
||||
UI_PUBLIC_URL: "http://localhost"
|
||||
|
||||
#monitor
|
||||
MONITOR_BRIDGE_NAME: "bridge"
|
||||
|
||||
@@ -33,6 +33,7 @@ COMMON_FOREIGN_GAS_PRICE_FACTOR: 1
|
||||
|
||||
## UI
|
||||
UI_TITLE: "TokenBridge UI app - %c"
|
||||
UI_OG_TITLE: "POA Bridge UI"
|
||||
UI_DESCRIPTION: "The TokenBridge serves as a method of transferring native tokens from the Ethereum Classic Network to the Ethereum network in a quick and cost-efficient manner."
|
||||
UI_PORT: 3001
|
||||
UI_HOME_EXPLORER_TX_TEMPLATE: https://blockscout.com/etc/mainnet/tx/%s
|
||||
@@ -41,6 +42,8 @@ UI_HOME_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/etc/mainnet/address/%s
|
||||
UI_FOREIGN_EXPLORER_ADDRESS_TEMPLATE: https://blockscout.com/eth/mainnet/address/%s
|
||||
UI_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000
|
||||
UI_STYLES: "classic"
|
||||
UI_PUBLIC_URL: "https://wetc.app"
|
||||
|
||||
## Monitor
|
||||
MONITOR_BRIDGE_NAME: "wetc"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user