├── .coveralls.yml ├── .dockerignore ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── .nvmrc ├── .prettierrc ├── CODE_OF_CONDUCT.md ├── CONFIGURATION.md ├── CONTRIBUTING.md ├── Dockerfile.e2e ├── LICENSE ├── README.md ├── alm-e2e ├── .eslintrc ├── package.json ├── run-tests.sh └── src │ ├── test.js │ └── utils │ └── utils.js ├── alm ├── .env.example ├── .eslintrc.js ├── .gitignore ├── Dockerfile ├── README.md ├── config-overrides.js ├── docker-compose.yml ├── load-env.sh ├── package.json ├── public │ ├── _redirects │ ├── favicon.ico │ ├── index.html │ ├── manifest.json │ └── robots.txt ├── scripts │ └── createSnapshots.js ├── src │ ├── App.tsx │ ├── abis │ │ ├── BridgeValidators.ts │ │ ├── ForeignAMB.ts │ │ ├── HomeAMB.ts │ │ └── index.ts │ ├── components │ │ ├── ConfirmationsContainer.tsx │ │ ├── ExecutionConfirmation.tsx │ │ ├── Form.tsx │ │ ├── MainPage.tsx │ │ ├── ManualExecutionButton.tsx │ │ ├── MessageSelector.tsx │ │ ├── NetworkTransactionSelector.tsx │ │ ├── StatusContainer.tsx │ │ ├── TransactionSelector.tsx │ │ ├── ValidatorsConfirmations.tsx │ │ └── commons │ │ │ ├── BackButton.tsx │ │ │ ├── Button.tsx │ │ │ ├── CloseIcon.tsx │ │ │ ├── ErrorAlert.tsx │ │ │ ├── ExplorerTxLink.tsx │ │ │ ├── InfoAlert.tsx │ │ │ ├── InfoIcon.tsx │ │ │ ├── Labels.tsx │ │ │ ├── LeftArrow.tsx │ │ │ ├── Loading.tsx │ │ │ ├── MultiLine.tsx │ │ │ ├── RadioButton.tsx │ │ │ ├── Table.tsx │ │ │ └── WarningAlert.tsx │ ├── config │ │ ├── constants.ts │ │ └── descriptions.ts │ ├── global.d.ts │ ├── hooks │ │ ├── useBlockConfirmations.ts │ │ ├── useBridgeContracts.ts │ │ ├── useClosestBlock.ts │ │ ├── useMessageConfirmations.ts │ │ ├── useNetwork.ts │ │ ├── useTransactionFinder.ts │ │ ├── useTransactionStatus.ts │ │ └── useValidatorContract.ts │ ├── index.tsx │ ├── react-app-env.d.ts │ ├── services │ │ ├── BlockNumberProvider.ts │ │ ├── SnapshotProvider.ts │ │ └── ValidatorsCache.ts │ ├── setupTests.ts │ ├── snapshots │ │ └── .gitkeep │ ├── state │ │ └── StateProvider.tsx │ ├── themes │ │ ├── Dark.tsx │ │ ├── GlobalStyle.tsx │ │ └── Light.ts │ └── utils │ │ ├── __tests__ │ │ ├── contracts.test.ts │ │ ├── explorer.test.ts │ │ ├── getConfirmationsForTx.test.ts │ │ └── getFinalizationEvent.test.ts │ │ ├── contract.ts │ │ ├── explorer.ts │ │ ├── getConfirmationsForTx.ts │ │ ├── getFinalizationEvent.ts │ │ ├── networks.ts │ │ ├── signatures.ts │ │ ├── validatorConfirmationHelpers.ts │ │ └── web3.ts └── tsconfig.json ├── audit ├── chainsecurity │ ├── FT-AMB-6.0.0-and-OmniBridge-1.1.0-contracts-security-assessment-report.pdf │ └── FT-OmniBridge-contracts-1.0.0-rc2-security-assessment-report.pdf ├── peppersec │ └── POA-Network-Token-bridge-security-assessment-report.pdf ├── quantstamp │ ├── POA-Network-Token-bridge-security-assessment-report.pdf │ └── POA-Network-TokenBridge-contracts-5.4.1-security-assessment-report.pdf └── smartdec │ └── POA-Network-TokenBridge-Contracts-v2-3-2-Security-Assessment.pdf ├── burner-wallet-plugin ├── .eslintrc.js ├── Dockerfile ├── README.md ├── docker-compose.yml ├── lerna.json ├── package.json ├── publish.md ├── staging │ ├── .env.example │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ ├── src │ │ ├── index.tsx │ │ └── react-app-env.d.ts │ └── tsconfig.json ├── testing │ ├── .env.example │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── manifest.json │ ├── src │ │ ├── LocalhostGateway.ts │ │ ├── index.tsx │ │ └── react-app-env.d.ts │ └── tsconfig.json ├── tokenbridge-bw-exchange │ ├── README.md │ ├── package.json │ ├── src │ │ ├── bridges │ │ │ ├── MOONBridge.ts │ │ │ ├── QDAIBridge.ts │ │ │ ├── WETCBridge.ts │ │ │ └── index.ts │ │ ├── burner-wallet │ │ │ ├── assets │ │ │ │ ├── BridgeableERC20Asset.ts │ │ │ │ ├── Dai.ts │ │ │ │ ├── ERC677Asset.ts │ │ │ │ ├── Etc.ts │ │ │ │ ├── MOON.ts │ │ │ │ ├── NativeMediatorAsset.ts │ │ │ │ ├── Wetc.ts │ │ │ │ ├── qDai.ts │ │ │ │ ├── sPOA.ts │ │ │ │ └── xMOON.ts │ │ │ ├── gateways │ │ │ │ └── TokenBridgeGateway.ts │ │ │ ├── index.ts │ │ │ └── pairs │ │ │ │ ├── Mediator.ts │ │ │ │ └── MediatorErcToNative.ts │ │ ├── index.ts │ │ └── utils │ │ │ ├── abis.ts │ │ │ ├── abis │ │ │ ├── ERC677.ts │ │ │ ├── ForeignBridgeNativeToErc.ts │ │ │ ├── HomeBridgeNativeToErc.ts │ │ │ ├── Mediator.ts │ │ │ ├── MediatorErcToNative.ts │ │ │ └── MediatorFeeManager.ts │ │ │ ├── index.ts │ │ │ └── utils.ts │ ├── test │ │ └── ERC677Asset.spec.ts │ ├── tsconfig.json │ └── tsconfig.testing.json ├── tsconfig.json └── yarn.lock ├── commons ├── .eslintrc ├── README.md ├── abis.js ├── constants.js ├── index.js ├── message.js ├── package.json ├── test │ ├── constants.test.js │ ├── gas.js │ ├── message.test.js │ └── validators.js └── utils.js ├── deployment-e2e ├── Dockerfile ├── README.md ├── molecule.sh └── molecule │ ├── monitor │ ├── Dockerfile.j2 │ ├── converge.yml │ ├── molecule.yml │ ├── run-checks.yml │ └── tests │ │ └── test_monitor.py │ ├── multiple │ ├── Dockerfile.j2 │ ├── molecule.yml │ └── tests │ │ └── test_multiple.py │ ├── oracle │ ├── Dockerfile.j2 │ ├── molecule.yml │ └── tests │ │ └── test_oracle.py │ ├── prepare.yml │ ├── repo │ ├── Dockerfile.j2 │ ├── converge.yml │ ├── molecule.yml │ └── tests │ │ ├── test_existing.py │ │ └── test_non_existing.py │ ├── tests │ └── test_all.py │ ├── ultimate-amb │ ├── Dockerfile.j2 │ └── molecule.yml │ ├── ultimate-commons │ ├── converge.yml │ └── oracle-docker-compose.yml │ └── ultimate-erc-to-native │ ├── Dockerfile.j2 │ └── molecule.yml ├── deployment ├── .yamllint ├── CONFIGURATION.md ├── EXECUTION.md ├── MONITOR.md ├── README.md ├── ansible.cfg ├── group_vars │ ├── amb.yml │ ├── dai.yml │ ├── erc-to-native.yml │ ├── example.yml │ └── ultimate.yml ├── hosts.yml.example ├── requirements.txt ├── roles │ ├── common │ │ ├── defaults │ │ │ └── main.yml │ │ ├── files │ │ │ └── daemon.json │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ ├── dependencies.yml │ │ │ ├── logging.yml │ │ │ ├── main.yml │ │ │ └── repo.yml │ │ └── templates │ │ │ ├── 30-docker.conf.j2 │ │ │ ├── 35-docker-remote-logging.conf.j2 │ │ │ └── docker-logs.j2 │ ├── monitor │ │ ├── defaults │ │ │ └── main.yml │ │ ├── meta │ │ │ └── main.yml │ │ ├── tasks │ │ │ ├── cron.yml │ │ │ ├── jumpbox.yml │ │ │ ├── logging.yml │ │ │ ├── main.yml │ │ │ ├── pre_config.yml │ │ │ └── servinstall.yml │ │ └── templates │ │ │ ├── .env.j2 │ │ │ ├── 33-monitor-docker.conf.j2 │ │ │ ├── 38-monitor-remote-logging.conf.j2 │ │ │ ├── config.env.j2 │ │ │ └── tokenbridge-monitor.j2 │ └── oracle │ │ ├── defaults │ │ └── main.yml │ │ ├── files │ │ └── modify_to_use_syslog.py │ │ ├── meta │ │ └── main.yml │ │ ├── tasks │ │ ├── jumpbox.yml │ │ ├── logging.yml │ │ ├── logging_by_syslog.yml │ │ ├── main.yml │ │ ├── post_config.yml │ │ ├── pre_config.yml │ │ └── servinstall.yml │ │ └── templates │ │ ├── .env.j2 │ │ ├── 31-oracle-docker.conf.j2 │ │ ├── 32-redis-docker.conf.j2 │ │ ├── 33-rabbit-docker.conf.j2 │ │ ├── 36-oracle-remote-logging.conf.j2 │ │ ├── key.j2 │ │ └── poabridge.j2 └── site.yml ├── e2e-commons ├── README.md ├── ULTIMATE.md ├── access-lists │ ├── allowance_list.txt │ └── block_list.txt ├── build.sh ├── components-envs │ ├── alm.env │ ├── monitor-amb.env │ ├── monitor-erc20-native.env │ ├── oracle-amb.env │ └── oracle-erc20-native.env ├── constants.json ├── contracts-envs │ ├── amb.env │ └── erc-to-native.env ├── docker-compose.yml ├── down.sh ├── pull.sh ├── scripts │ ├── blocks.js │ └── deploy.sh ├── ultimate.png ├── up.sh └── utils.js ├── monitor-e2e ├── .eslintrc ├── README.md ├── package.json ├── periodically-check-all.sh ├── run-tests.sh ├── test │ ├── amb.js │ ├── common.js │ └── ercToNative.js ├── utils.js └── wait-for-monitor.sh ├── monitor ├── .env.example ├── .eslintrc ├── Dockerfile ├── README.md ├── alerts.js ├── cache │ └── .gitkeep ├── checkWorker.js ├── checkWorker2.js ├── checkWorker3.js ├── configs │ └── .gitkeep ├── crontab.example ├── detectFailures.js ├── detectMediators.js ├── docker-compose-build.yml ├── docker-compose.yml ├── eventsStats.js ├── getBalances.js ├── getShortEventStats.js ├── index.js ├── logger.js ├── metricsWorker.js ├── package.json ├── prometheusMetrics.js ├── responses │ └── .gitkeep ├── scripts │ └── getBridgeStats.sh ├── test-srv.js ├── test │ └── message.test.js ├── utils │ ├── events.js │ ├── file.js │ ├── getValidatorsList.js │ ├── message.js │ ├── web3.js │ └── web3Cache.js └── validators.js ├── oracle-e2e ├── .eslintrc ├── README.md ├── package.json ├── run-tests.sh ├── scripts │ └── generate-amb-tx.js └── test │ ├── amb.js │ ├── ercToNative.js │ ├── mocha.opts │ └── utils.js ├── oracle ├── .env.example ├── .eslintrc ├── Dockerfile ├── ERC_TO_NATIVE.png ├── README.md ├── config │ ├── affirmation-request-watcher.config.js │ ├── base.config.js │ ├── collected-signatures-watcher.config.js │ ├── foreign-mev-sender.config.js │ ├── foreign-sender.config.js │ ├── home-sender.config.js │ ├── information-request-watcher.config.js │ ├── mev-collected-signatures-watcher.config.js │ ├── shutdown-manager.config.js │ ├── signature-request-watcher.config.js │ └── transfer-watcher.config.js ├── docker-compose-amb.yml ├── docker-compose-build.yml ├── docker-compose-helpers.yml ├── docker-compose-transfer.yml ├── docker-compose.yml ├── docs │ └── stress-testing.md ├── env.js ├── esController.sol ├── package.json ├── reset-lastBlock.sh ├── scripts │ ├── .eslintrc │ ├── compute-stats.js │ ├── erc20_to_native │ │ ├── sendForeign.js │ │ └── sendHome.js │ ├── getValidatorStartBlocks.js │ ├── interestFetcher.js │ ├── privateKeyToAddress.js │ ├── resetLastBlock.js │ ├── signPendingMessages.js │ ├── start-worker.sh │ └── utils │ │ └── utils.js ├── src │ ├── confirmRelay.js │ ├── events │ │ ├── processAMBAffirmationRequests │ │ │ ├── estimateGas.js │ │ │ └── index.js │ │ ├── processAMBCollectedSignatures │ │ │ ├── estimateGas.js │ │ │ └── index.js │ │ ├── processAMBCollectedSignaturesMEV │ │ │ └── index.js │ │ ├── processAMBInformationRequests │ │ │ ├── calls │ │ │ │ ├── ethBlockNumber.js │ │ │ │ ├── ethCall.js │ │ │ │ ├── ethGetBalance.js │ │ │ │ ├── ethGetBlockByHash.js │ │ │ │ ├── ethGetBlockByNumber.js │ │ │ │ ├── ethGetStorageAt.js │ │ │ │ ├── ethGetTransactionByHash.js │ │ │ │ ├── ethGetTransactionCount.js │ │ │ │ ├── ethGetTransactionReceipt.js │ │ │ │ └── serializers.js │ │ │ ├── estimateGas.js │ │ │ └── index.js │ │ ├── processAMBSignatureRequests │ │ │ └── index.js │ │ ├── processAffirmationRequests │ │ │ ├── estimateGas.js │ │ │ └── index.js │ │ ├── processCollectedSignatures │ │ │ ├── estimateGas.js │ │ │ └── index.js │ │ ├── processSignatureRequests │ │ │ ├── estimateGas.js │ │ │ └── index.js │ │ └── processTransfers │ │ │ └── index.js │ ├── mevSender.js │ ├── mevWatcher.js │ ├── sender.js │ ├── services │ │ ├── HttpListProvider.js │ │ ├── RedundantHttpListProvider.js │ │ ├── SafeEthLogsProvider.js │ │ ├── amqpClient.js │ │ ├── blockFinder.js │ │ ├── gasPrice.js │ │ ├── injectedLogger.js │ │ ├── logger.js │ │ ├── redisClient.js │ │ ├── shutdownState.js │ │ └── web3.js │ ├── shutdownManager.js │ ├── tx │ │ ├── sendTx.js │ │ └── web3.js │ ├── utils │ │ ├── constants.js │ │ ├── errors.js │ │ ├── message.js │ │ ├── mev.js │ │ ├── tokenState.js │ │ ├── tryEach.js │ │ └── utils.js │ └── watcher.js └── test │ ├── .eslintrc │ ├── amqp.test.js │ ├── blockFinder.test.js │ ├── gasPrice.test.js │ ├── message.test.js │ ├── processAffirmationRequests.test.js │ ├── processCollectedSignatures.test.js │ ├── processSignatureRequests.test.js │ ├── test.env │ ├── tryEach.test.js │ └── utils.test.js ├── package.json ├── parity ├── Dockerfile ├── Dockerfile-foreign ├── chain-foreign.json ├── chain.json └── foreign_mocks │ └── cDAIMock.sol └── yarn.lock /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: circleci 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/submodules 3 | **/Dockerfile 4 | **/.dockerignore 5 | **/docker-compose.yml 6 | .gitmodules 7 | **/.git 8 | **/docs 9 | **/*.md 10 | 11 | monitor/**/*.env* 12 | oracle/**/*.env* 13 | !**/.env.example 14 | 15 | contracts/test 16 | contracts/build 17 | oracle/test 18 | monitor/test 19 | monitor/responses 20 | monitor/cache/* 21 | commons/test 22 | oracle/**/*.png 23 | oracle/**/*.jpg 24 | audit 25 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | submodules 3 | coverage 4 | lib 5 | dist 6 | build 7 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:prettier/recommended" 4 | ], 5 | "plugins": ["prettier"], 6 | "rules": { 7 | "prettier/prettier": "error", 8 | "arrow-body-style": "off", 9 | "func-names": "off", 10 | "no-await-in-loop": "off", 11 | "no-console": "off", 12 | "no-else-return": "off", 13 | "no-param-reassign": "off", 14 | "no-plusplus": "off", 15 | "no-restricted-syntax": "off", 16 | "no-shadow": "off", 17 | "no-use-before-define": ["error", { "functions": false }], 18 | "import/no-dynamic-require": "off", 19 | "prefer-template": "off", 20 | "no-underscore-dangle": "off" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # testing 5 | coverage 6 | 7 | # production 8 | build 9 | dist 10 | 11 | # misc 12 | .DS_Store 13 | *.env* 14 | !.env.example 15 | .idea 16 | .nyc_output 17 | logs/ 18 | 19 | 20 | *.err 21 | *.out 22 | data 23 | 24 | # contracts build 25 | ui/src/contracts 26 | oracle/abis 27 | monitor/abis 28 | 29 | # compiled css 30 | *.css 31 | 32 | npm-debug.log* 33 | yarn-debug.log* 34 | yarn-error.log* 35 | 36 | #deployment 37 | *.retry 38 | hosts.yml 39 | hosts 40 | *.log 41 | Vagrantfile 42 | vagrant-hosts.yml 43 | .vagrant 44 | deployment/venv 45 | __pycache__ 46 | 47 | #monitor 48 | monitor/responses/* 49 | monitor/cache/* 50 | !monitor/cache/.gitkeep 51 | !monitor/.gitkeep 52 | 53 | # Local Netlify folder 54 | .netlify -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "contracts"] 2 | path = contracts 3 | url = https://github.com/poanetwork/tokenbridge-contracts.git 4 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 12.22 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": false, 3 | "singleQuote": true, 4 | "printWidth": 120, 5 | "bracketSpacing": true 6 | } 7 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | Thank your for contributing to this project! We welcome collaborators and expect users to follow our [code of conduct](CODE_OF_CONDUCT.md) when submitting code or comments. 4 | 5 | 1. Fork the repo ( https://github.com/poanetwork/tokenbridge/fork ). 6 | 2. Create your feature branch (`git checkout -b my-new-feature`). 7 | 3. Write tests that cover your work. 8 | 4. Commit your changes (`git commit -am 'Add some feature'`). 9 | 5. Push to your branch (`git push origin my-new-feature`). 10 | 6. Create a new PR (Pull Request). 11 | 12 | ### General 13 | 14 | * Commits should be one logical change that still allows all tests to pass. We prefer smaller commits if there could be two levels of logic grouping. The goal is to provide future contributors (including your future self) the reasoning behind your changes and allow them to cherry-pick, patch or port those changes in isolation to other branches or forks. 15 | * If during your PR you reveal a pre-existing bug and know how to fix it: 16 | 1. If you can isolate the bug, fix it in a separate PR. 17 | 2. If the fix depends on your other commits, add it in a separate commit to the same PR. 18 | 19 | In either case, try to write a regression test that fails because of the bug but passes with your fix. 20 | 21 | ### Issues 22 | Creating and discussing [Issues](https://github.com/poanetwork/tokenbridge/issues) provides significant value to the project. If you find a bug you can report it in an Issue. 23 | 24 | ### Pull Requests 25 | All pull requests should include: 26 | * A clear, readable description of the purpose of the PR 27 | * A clear, readable description of changes 28 | * Any additional concerns or comments (optional) 29 | -------------------------------------------------------------------------------- /Dockerfile.e2e: -------------------------------------------------------------------------------- 1 | FROM node:12 as contracts 2 | 3 | WORKDIR /mono 4 | 5 | COPY contracts/package.json contracts/package-lock.json ./contracts/ 6 | 7 | WORKDIR /mono/contracts 8 | RUN npm install --only=prod 9 | 10 | COPY ./contracts/truffle-config.js ./ 11 | COPY ./contracts/contracts ./contracts 12 | RUN npm run compile 13 | 14 | FROM node:12 15 | 16 | WORKDIR /mono 17 | COPY package.json . 18 | COPY --from=contracts /mono/contracts/build ./contracts/build 19 | COPY commons/package.json ./commons/ 20 | COPY oracle-e2e/package.json ./oracle-e2e/ 21 | COPY monitor-e2e/package.json ./monitor-e2e/ 22 | COPY oracle/src/utils/constants.js ./oracle/src/utils/constants.js 23 | 24 | COPY yarn.lock . 25 | RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production 26 | 27 | COPY ./contracts/deploy ./contracts/deploy 28 | RUN yarn install:deploy 29 | 30 | COPY commons/ ./commons/ 31 | COPY oracle-e2e/ ./oracle-e2e/ 32 | COPY monitor-e2e/ ./monitor-e2e/ 33 | COPY e2e-commons/ ./e2e-commons/ 34 | -------------------------------------------------------------------------------- /alm-e2e/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:node/recommended", 4 | "airbnb-base", 5 | "../.eslintrc" 6 | ], 7 | "plugins": ["node", "jest"], 8 | "env": { 9 | "jest/globals": true 10 | }, 11 | "globals": { 12 | "page": true, 13 | "browser": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /alm-e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alm-e2e", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "test": "jest --detectOpenHandles", 7 | "lint": "eslint . --ignore-path ../.eslintignore", 8 | "lint:fix": "eslint . --fix" 9 | }, 10 | "dependencies": { 11 | "jest": "24.7.1", 12 | "jest-puppeteer": "^4.4.0", 13 | "puppeteer": "^5.2.1" 14 | }, 15 | "jest": { 16 | "preset": "jest-puppeteer" 17 | }, 18 | "devDependencies": { 19 | "eslint-plugin-jest": "^23.18.0" 20 | }, 21 | "engines": { 22 | "node": ">= 12.22" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /alm-e2e/run-tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd $(dirname $0) 3 | 4 | ../e2e-commons/up.sh deploy generate-amb-tx blocks alm alm-e2e 5 | 6 | # run oracle amb e2e tests to generate transactions for alm 7 | docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run alm 8 | 9 | yarn test 10 | rc=$? 11 | 12 | ../e2e-commons/down.sh 13 | exit $rc 14 | -------------------------------------------------------------------------------- /alm-e2e/src/test.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer') 2 | const { waitUntil } = require('./utils/utils') 3 | 4 | jest.setTimeout(60000) 5 | 6 | const statusText = 'Success' 7 | const statusSelector = 'label[data-id="status"]' 8 | 9 | const homeToForeignTxURL = 'http://localhost:3004/77/0x295efbe6ae98937ef35d939376c9bd752b4dc6f6899a9d5ddd6a57cea3d76c89' 10 | const foreignToHomeTxURL = 'http://localhost:3004/42/0x7262f7dbe6c30599edded2137fbbe93c271b37f5c54dd27f713f0cf510e3b4dd' 11 | 12 | describe('ALM', () => { 13 | let browser 14 | let page 15 | 16 | beforeAll(async () => { 17 | browser = await puppeteer.launch() 18 | page = await browser.newPage() 19 | }) 20 | 21 | afterAll(async () => { 22 | await browser.close() 23 | }) 24 | 25 | it('should be titled "AMB Live Monitoring"', async () => { 26 | await page.goto(foreignToHomeTxURL) 27 | 28 | await expect(page.title()).resolves.toMatch('AMB Live Monitoring') 29 | }) 30 | it('should display information of foreign to home transaction', async () => { 31 | await page.goto(foreignToHomeTxURL) 32 | 33 | await page.waitForSelector(statusSelector) 34 | await waitUntil(async () => { 35 | const element = await page.$(statusSelector) 36 | const text = await page.evaluate(element => element.textContent, element) 37 | return text === statusText 38 | }) 39 | }) 40 | it('should display information of home to foreign transaction', async () => { 41 | await page.goto(homeToForeignTxURL) 42 | 43 | await page.waitForSelector(statusSelector) 44 | await waitUntil(async () => { 45 | const element = await page.$(statusSelector) 46 | const text = await page.evaluate(element => element.textContent, element) 47 | return text === statusText 48 | }) 49 | }) 50 | }) 51 | -------------------------------------------------------------------------------- /alm-e2e/src/utils/utils.js: -------------------------------------------------------------------------------- 1 | const waitUntil = async (predicate, step = 100, timeout = 20000) => { 2 | const stopTime = Date.now() + timeout 3 | while (Date.now() <= stopTime) { 4 | const result = await predicate() 5 | if (result) { 6 | return result 7 | } 8 | await new Promise(resolve => setTimeout(resolve, step)) // sleep 9 | } 10 | throw new Error(`waitUntil timed out after ${timeout} ms`) 11 | } 12 | 13 | module.exports = { 14 | waitUntil 15 | } 16 | -------------------------------------------------------------------------------- /alm/.env.example: -------------------------------------------------------------------------------- 1 | COMMON_HOME_BRIDGE_ADDRESS=0xFe446bEF1DbF7AFE24E81e05BC8B271C1BA9a560 2 | COMMON_FOREIGN_BRIDGE_ADDRESS=0xFe446bEF1DbF7AFE24E81e05BC8B271C1BA9a560 3 | 4 | COMMON_HOME_RPC_URL=https://sokol.poa.network 5 | COMMON_FOREIGN_RPC_URL=https://kovan.infura.io/v3/ 6 | 7 | ALM_HOME_NETWORK_NAME=Sokol Testnet 8 | ALM_FOREIGN_NETWORK_NAME=Kovan Testnet 9 | 10 | ALM_HOME_EXPLORER_TX_TEMPLATE=https://blockscout.com/poa/sokol/tx/%s 11 | ALM_FOREIGN_EXPLORER_TX_TEMPLATE=https://blockscout.com/eth/kovan/tx/%s 12 | 13 | ALM_HOME_EXPLORER_API=https://blockscout.com/poa/sokol/api 14 | ALM_FOREIGN_EXPLORER_API=https://kovan.etherscan.io/api?apikey=YourApiKeyToken 15 | PORT=8080 16 | -------------------------------------------------------------------------------- /alm/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | "react-app", 4 | "../.eslintrc" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /alm/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | src/snapshots/*.json 4 | 5 | # dependencies 6 | /node_modules 7 | /.pnp 8 | .pnp.js 9 | 10 | # testing 11 | /coverage 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | .env.local 19 | .env.development.local 20 | .env.test.local 21 | .env.production.local 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /alm/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 as contracts 2 | 3 | WORKDIR /mono 4 | 5 | COPY contracts/package.json contracts/package-lock.json ./contracts/ 6 | 7 | WORKDIR /mono/contracts 8 | RUN npm install --only=prod 9 | 10 | COPY ./contracts/truffle-config.js ./ 11 | COPY ./contracts/contracts ./contracts 12 | RUN npm run compile 13 | 14 | FROM node:12 as alm-builder 15 | 16 | WORKDIR /mono 17 | COPY package.json . 18 | COPY --from=contracts /mono/contracts/build ./contracts/build 19 | COPY commons/package.json ./commons/ 20 | COPY alm/package.json ./alm/ 21 | COPY yarn.lock . 22 | RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile 23 | 24 | COPY ./commons ./commons 25 | COPY ./alm ./alm 26 | 27 | ARG DOT_ENV_PATH=./alm/.env 28 | COPY ${DOT_ENV_PATH} ./alm/.env 29 | 30 | WORKDIR /mono/alm 31 | RUN yarn run build 32 | 33 | 34 | FROM node:12 as alm-production 35 | RUN yarn global add serve 36 | WORKDIR /app 37 | COPY --from=alm-builder /mono/alm/build . 38 | CMD serve -p $PORT -s . 39 | -------------------------------------------------------------------------------- /alm/config-overrides.js: -------------------------------------------------------------------------------- 1 | const { override, disableEsLint } = require('customize-cra') 2 | const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin') 3 | 4 | const disableModuleScopePlugin = () => config => { 5 | config.resolve.plugins = config.resolve.plugins.filter(plugin => !(plugin instanceof ModuleScopePlugin)) 6 | return config 7 | } 8 | 9 | module.exports = override(disableEsLint(), disableModuleScopePlugin()) 10 | -------------------------------------------------------------------------------- /alm/docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '2.4' 3 | services: 4 | alm: 5 | build: 6 | context: .. 7 | dockerfile: alm/Dockerfile 8 | ports: 9 | - "${PORT}:${PORT}" 10 | env_file: ./.env 11 | environment: 12 | - NODE_ENV=production 13 | restart: unless-stopped 14 | entrypoint: serve -p ${PORT} -s . 15 | -------------------------------------------------------------------------------- /alm/load-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | while read line; do 4 | if [ "$line" = "" ]; then 5 | : # Skip empty lines 6 | elif [[ "$line" =~ \#.* ]]; then 7 | : # Skip comment lines 8 | elif [[ "$line" =~ "UI_PORT"* ]]; then 9 | eval $line 10 | export PORT="$UI_PORT" 11 | else 12 | export "REACT_APP_$line" 13 | fi 14 | done < '.env' 15 | 16 | $* 17 | -------------------------------------------------------------------------------- /alm/public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 -------------------------------------------------------------------------------- /alm/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/alm/public/favicon.ico -------------------------------------------------------------------------------- /alm/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "ALM", 3 | "name": "AMB Live Monitoring", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /alm/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /alm/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { BrowserRouter } from 'react-router-dom' 3 | import { Web3ReactProvider } from '@web3-react/core' 4 | import Web3 from 'web3' 5 | import { MainPage } from './components/MainPage' 6 | import { StateProvider } from './state/StateProvider' 7 | 8 | function App() { 9 | return ( 10 | 11 | new Web3(provider)}> 12 | 13 | 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | export default App 21 | -------------------------------------------------------------------------------- /alm/src/abis/index.ts: -------------------------------------------------------------------------------- 1 | export { default as HOME_AMB_ABI } from './HomeAMB' 2 | export { default as FOREIGN_AMB_ABI } from './ForeignAMB' 3 | export { default as BRIDGE_VALIDATORS_ABI } from './BridgeValidators' 4 | -------------------------------------------------------------------------------- /alm/src/components/commons/BackButton.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom' 2 | import { LeftArrow } from './LeftArrow' 3 | import React from 'react' 4 | import styled from 'styled-components' 5 | 6 | const StyledButton = styled.button` 7 | color: var(--button-color); 8 | border-color: var(--font-color); 9 | margin-top: 10px; 10 | &:focus { 11 | outline: var(--button-color); 12 | } 13 | ` 14 | 15 | const BackLabel = styled.label` 16 | margin-left: 5px; 17 | cursor: pointer; 18 | ` 19 | 20 | export interface BackButtonParam { 21 | onBackToMain: () => void 22 | } 23 | 24 | export const BackButton = ({ onBackToMain }: BackButtonParam) => ( 25 |
26 |
27 | 28 | 29 | 30 | Search another transaction 31 | 32 | 33 |
34 |
35 | ) 36 | -------------------------------------------------------------------------------- /alm/src/components/commons/Button.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const Button = styled.button` 4 | height: 36px; 5 | color: var(--button-color); 6 | border-color: var(--button-color); 7 | &:focus { 8 | outline: var(--button-color); 9 | } 10 | ` 11 | -------------------------------------------------------------------------------- /alm/src/components/commons/CloseIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const CloseIcon = ({ color }: { color?: string }) => ( 4 | 18 | ) 19 | -------------------------------------------------------------------------------- /alm/src/components/commons/ErrorAlert.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { InfoIcon } from './InfoIcon' 4 | import { CloseIcon } from './CloseIcon' 5 | import { ExplorerTxLink } from './ExplorerTxLink' 6 | 7 | const StyledErrorAlert = styled.div` 8 | border: 1px solid var(--failed-color); 9 | border-radius: 4px; 10 | margin-bottom: 20px; 11 | padding-top: 10px; 12 | ` 13 | 14 | const CloseIconContainer = styled.div` 15 | cursor: pointer; 16 | ` 17 | 18 | const TextContainer = styled.div` 19 | white-space: pre-wrap; 20 | flex-direction: column; 21 | ` 22 | 23 | export const ErrorAlert = ({ onClick, error }: { onClick: () => void; error: string }) => { 24 | const errorArray = error.split('%link') 25 | const text = errorArray[0] 26 | let link 27 | if (errorArray.length > 1) { 28 | link = ( 29 | 30 | {errorArray[1]} 31 | 32 | ) 33 | } 34 | return ( 35 |
36 | 37 | 38 | 39 | {text} 40 | {link} 41 | 42 | 43 | 44 | 45 | 46 |
47 | ) 48 | } 49 | -------------------------------------------------------------------------------- /alm/src/components/commons/ExplorerTxLink.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const ExplorerTxLink = styled.a` 4 | color: var(--link-color); 5 | text-decoration: underline; 6 | font-weight: bold; 7 | ` 8 | -------------------------------------------------------------------------------- /alm/src/components/commons/InfoAlert.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { InfoIcon } from './InfoIcon' 4 | import { CloseIcon } from './CloseIcon' 5 | 6 | const StyledInfoAlert = styled.div` 7 | border: 1px solid var(--button-color); 8 | border-radius: 4px; 9 | margin-bottom: 20px; 10 | padding-top: 10px; 11 | ` 12 | 13 | const CloseIconContainer = styled.div` 14 | cursor: pointer; 15 | ` 16 | 17 | const TextContainer = styled.div` 18 | flex-direction: column; 19 | ` 20 | 21 | export const InfoAlert = ({ onClick, children }: { onClick: () => void; children: React.ReactChild[] }) => ( 22 |
23 | 24 | 25 | {children} 26 | 27 | 28 | 29 | 30 |
31 | ) 32 | -------------------------------------------------------------------------------- /alm/src/components/commons/InfoIcon.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export const InfoIcon = ({ color }: { color?: string }) => ( 4 | 16 | ) 17 | -------------------------------------------------------------------------------- /alm/src/components/commons/Labels.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const SuccessLabel = styled.label` 4 | color: var(--success-color); 5 | background-color: var(--success-bg-color); 6 | padding: 0.4rem 0.7rem; 7 | border-radius: 4px; 8 | ` 9 | 10 | export const GreyLabel = styled.label` 11 | color: var(--not-required-color); 12 | background-color: var(--not-required-bg-color); 13 | padding: 0.4rem 0.7rem; 14 | border-radius: 4px; 15 | ` 16 | 17 | export const RedLabel = styled.label` 18 | color: var(--failed-color); 19 | background-color: var(--failed-bg-color); 20 | padding: 0.4rem 0.7rem; 21 | border-radius: 4px; 22 | ` 23 | -------------------------------------------------------------------------------- /alm/src/components/commons/LeftArrow.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { useContext } from 'react' 3 | import { ThemeContext } from 'styled-components' 4 | 5 | export const LeftArrow = () => { 6 | const themeContext = useContext(ThemeContext) 7 | return ( 8 | 17 | 18 | 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /alm/src/components/commons/MultiLine.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const MultiLine = styled.div` 4 | white-space: pre-wrap; 5 | ` 6 | -------------------------------------------------------------------------------- /alm/src/components/commons/RadioButton.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const RadioButtonLabel = styled.label` 4 | padding-left: 5px; 5 | ` 6 | 7 | export const RadioButtonContainer = styled.div` 8 | padding: 10px; 9 | ` 10 | -------------------------------------------------------------------------------- /alm/src/components/commons/Table.tsx: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components' 2 | 3 | export const Thead = styled.thead` 4 | border-bottom: 2px solid #9e9e9e; 5 | ` 6 | 7 | export const StatusTd = styled.td` 8 | width: 150px; 9 | ` 10 | 11 | export const AgeTd = styled.td` 12 | width: 180px; 13 | ` 14 | -------------------------------------------------------------------------------- /alm/src/components/commons/WarningAlert.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | import { InfoIcon } from './InfoIcon' 4 | import { CloseIcon } from './CloseIcon' 5 | 6 | const StyledErrorAlert = styled.div` 7 | border: 1px solid var(--warning-color); 8 | border-radius: 4px; 9 | margin-bottom: 20px; 10 | padding-top: 10px; 11 | ` 12 | 13 | const CloseIconContainer = styled.div` 14 | cursor: pointer; 15 | ` 16 | 17 | const TextContainer = styled.div` 18 | white-space: pre-wrap; 19 | flex-direction: column; 20 | ` 21 | 22 | export const WarningAlert = ({ onClick, error }: { onClick: () => void; error: string }) => { 23 | return ( 24 |
25 | 26 | 27 | {error} 28 | 29 | 30 | 31 | 32 |
33 | ) 34 | } 35 | -------------------------------------------------------------------------------- /alm/src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare type Maybe = T | null 2 | -------------------------------------------------------------------------------- /alm/src/hooks/useBridgeContracts.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { HOME_AMB_ABI, FOREIGN_AMB_ABI } from '../abis' 3 | import { FOREIGN_BRIDGE_ADDRESS, HOME_BRIDGE_ADDRESS } from '../config/constants' 4 | import { Contract } from 'web3-eth-contract' 5 | import Web3 from 'web3' 6 | 7 | export interface useBridgeContractsParams { 8 | homeWeb3: Web3 9 | foreignWeb3: Web3 10 | } 11 | 12 | export const useBridgeContracts = ({ homeWeb3, foreignWeb3 }: useBridgeContractsParams) => { 13 | const [homeBridge, setHomeBridge] = useState>(null) 14 | const [foreignBridge, setForeignBridge] = useState>(null) 15 | 16 | useEffect( 17 | () => { 18 | if (!homeWeb3) return 19 | const homeContract = new homeWeb3.eth.Contract(HOME_AMB_ABI, HOME_BRIDGE_ADDRESS) 20 | setHomeBridge(homeContract) 21 | }, 22 | [homeWeb3] 23 | ) 24 | 25 | useEffect( 26 | () => { 27 | if (!foreignWeb3) return 28 | const foreignContract = new foreignWeb3.eth.Contract(FOREIGN_AMB_ABI, FOREIGN_BRIDGE_ADDRESS) 29 | setForeignBridge(foreignContract) 30 | }, 31 | [foreignWeb3] 32 | ) 33 | 34 | return { 35 | homeBridge, 36 | foreignBridge 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /alm/src/hooks/useNetwork.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { getChainId, getWeb3 } from '../utils/web3' 3 | import { SnapshotProvider } from '../services/SnapshotProvider' 4 | 5 | export const useNetwork = (url: string, snapshotProvider: SnapshotProvider) => { 6 | const [loading, setLoading] = useState(true) 7 | const [chainId, setChainId] = useState(0) 8 | const web3 = getWeb3(url) 9 | 10 | useEffect( 11 | () => { 12 | setLoading(true) 13 | const getWeb3ChainId = async () => { 14 | const id = await getChainId(web3, snapshotProvider) 15 | setChainId(id) 16 | setLoading(false) 17 | } 18 | getWeb3ChainId() 19 | }, 20 | [web3, snapshotProvider] 21 | ) 22 | 23 | return { 24 | web3, 25 | chainId, 26 | loading 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /alm/src/hooks/useTransactionFinder.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import { TransactionReceipt } from 'web3-eth' 3 | import { HOME_RPC_POLLING_INTERVAL, TRANSACTION_STATUS } from '../config/constants' 4 | import Web3 from 'web3' 5 | 6 | export const useTransactionFinder = ({ txHash, web3 }: { txHash: string; web3: Maybe }) => { 7 | const [status, setStatus] = useState(TRANSACTION_STATUS.UNDEFINED) 8 | const [receipt, setReceipt] = useState>(null) 9 | 10 | useEffect( 11 | () => { 12 | if (!txHash || !web3) return 13 | 14 | let timeoutId: number 15 | 16 | const getReceipt = async () => { 17 | const txReceipt = await web3.eth.getTransactionReceipt(txHash) 18 | setReceipt(txReceipt) 19 | 20 | if (!txReceipt) { 21 | setStatus(TRANSACTION_STATUS.NOT_FOUND) 22 | timeoutId = setTimeout(getReceipt, HOME_RPC_POLLING_INTERVAL) 23 | } else { 24 | setStatus(TRANSACTION_STATUS.FOUND) 25 | } 26 | } 27 | 28 | getReceipt() 29 | 30 | return () => clearTimeout(timeoutId) 31 | }, 32 | [txHash, web3] 33 | ) 34 | 35 | return { 36 | status, 37 | receipt 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /alm/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import { ThemeProvider } from 'styled-components' 4 | import { GlobalStyle } from './themes/GlobalStyle' 5 | import App from './App' 6 | import Light from './themes/Light' 7 | 8 | ReactDOM.render( 9 | 10 | 11 | 12 | 13 | 14 | , 15 | document.getElementById('root') 16 | ) 17 | -------------------------------------------------------------------------------- /alm/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /alm/src/services/ValidatorsCache.ts: -------------------------------------------------------------------------------- 1 | import { ConfirmationParam } from '../hooks/useMessageConfirmations' 2 | 3 | class ValidatorsCache { 4 | private readonly store: { [key: string]: boolean } 5 | private readonly dataStore: { [key: string]: ConfirmationParam } 6 | 7 | constructor() { 8 | this.store = {} 9 | this.dataStore = {} 10 | } 11 | 12 | get(key: string) { 13 | return this.store[key] 14 | } 15 | 16 | set(key: string, value: boolean) { 17 | this.store[key] = value 18 | } 19 | 20 | getData(key: string) { 21 | return this.dataStore[key] 22 | } 23 | 24 | setData(key: string, value: ConfirmationParam) { 25 | this.dataStore[key] = value 26 | } 27 | } 28 | 29 | export default new ValidatorsCache() 30 | -------------------------------------------------------------------------------- /alm/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect' 6 | -------------------------------------------------------------------------------- /alm/src/snapshots/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/alm/src/snapshots/.gitkeep -------------------------------------------------------------------------------- /alm/src/themes/Dark.tsx: -------------------------------------------------------------------------------- 1 | const theme = { 2 | backgroundColor: '#121212', 3 | fontColor: '#f5f5f5', 4 | buttonColor: '#f5f5f5', 5 | colorPrimary: '#272727', 6 | colorGrey: '#272727', 7 | colorLightGrey: '#272727', 8 | linkColor: '#ffffff', 9 | success: { 10 | textColor: '#00c9a7', 11 | backgroundColor: '#004d40' 12 | }, 13 | notRequired: { 14 | textColor: '#bdbdbd', 15 | backgroundColor: '#424242' 16 | }, 17 | failed: { 18 | textColor: '#EF5350', 19 | backgroundColor: '#4E342E' 20 | } 21 | } 22 | export default theme 23 | -------------------------------------------------------------------------------- /alm/src/themes/GlobalStyle.tsx: -------------------------------------------------------------------------------- 1 | import { createGlobalStyle } from 'styled-components' 2 | 3 | import theme from './Light' 4 | 5 | type ThemeType = typeof theme 6 | 7 | export const GlobalStyle = createGlobalStyle<{ theme: ThemeType }>` 8 | body { 9 | margin: 0; 10 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 11 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 12 | sans-serif; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | } 16 | 17 | :root { 18 | --bg-color: ${props => props.theme.backgroundColor}; 19 | --font-color: ${props => props.theme.fontColor}; 20 | --button-color: ${props => props.theme.buttonColor}; 21 | --color-primary: ${props => props.theme.colorPrimary}; 22 | --color-grey: ${props => props.theme.colorGrey}; 23 | --color-lightGrey: ${props => props.theme.colorLightGrey}; 24 | --link-color: ${props => props.theme.linkColor}; 25 | --success-color: ${props => props.theme.success.textColor}; 26 | --success-bg-color: ${props => props.theme.success.backgroundColor}; 27 | --not-required-color: ${props => props.theme.notRequired.textColor}; 28 | --not-required-bg-color: ${props => props.theme.notRequired.backgroundColor}; 29 | --failed-color: ${props => props.theme.failed.textColor}; 30 | --failed-bg-color: ${props => props.theme.failed.backgroundColor}; 31 | --warning-color: ${props => props.theme.warning.textColor}; 32 | --warning-bg-color: ${props => props.theme.warning.backgroundColor}; 33 | } 34 | ` 35 | -------------------------------------------------------------------------------- /alm/src/themes/Light.ts: -------------------------------------------------------------------------------- 1 | const theme = { 2 | backgroundColor: '#FFFFFF', 3 | fontColor: 'rgba(0, 0, 0, 0.65)', 4 | buttonColor: '#1890ff', 5 | colorPrimary: '#BDBDBD', 6 | colorGrey: '#1890ff', 7 | colorLightGrey: '#1890ff', 8 | linkColor: '#1890ff', 9 | success: { 10 | textColor: '#388E3C', 11 | backgroundColor: 'rgba(0,201,167,.1)' 12 | }, 13 | notRequired: { 14 | textColor: '#77838f', 15 | backgroundColor: 'rgba(119,131,143,.1)' 16 | }, 17 | failed: { 18 | textColor: '#de4437', 19 | backgroundColor: 'rgba(222,68,55,.1)' 20 | }, 21 | warning: { 22 | textColor: '#ffa758', 23 | backgroundColor: 'rgba(222,68,55,.1)' 24 | } 25 | } 26 | export default theme 27 | -------------------------------------------------------------------------------- /alm/src/utils/networks.ts: -------------------------------------------------------------------------------- 1 | import { formatDistance } from 'date-fns' 2 | import { 3 | CONFIRMATIONS_STATUS_DESCRIPTION, 4 | CONFIRMATIONS_STATUS_DESCRIPTION_HOME, 5 | TRANSACTION_STATUS_DESCRIPTION 6 | } from '../config/descriptions' 7 | import { FOREIGN_EXPLORER_TX_TEMPLATE, HOME_EXPLORER_TX_TEMPLATE } from '../config/constants' 8 | 9 | export const validTxHash = (txHash: string) => /^0x[a-fA-F0-9]{64}$/.test(txHash) 10 | 11 | export const formatTxHash = (txHash: string) => `${txHash.substring(0, 6)}...${txHash.substring(txHash.length - 4)}` 12 | 13 | export const getExplorerTxUrl = (txHash: string, isHome: boolean) => { 14 | const template = isHome ? HOME_EXPLORER_TX_TEMPLATE : FOREIGN_EXPLORER_TX_TEMPLATE 15 | return template.replace('%s', txHash) 16 | } 17 | 18 | export const formatTxHashExtended = (txHash: string) => 19 | `${txHash.substring(0, 10)}...${txHash.substring(txHash.length - 8)}` 20 | 21 | export const formatTimestamp = (timestamp: number): string => { 22 | const txDate = new Date(0).setUTCSeconds(timestamp) 23 | return formatDistance(txDate, new Date(), { 24 | addSuffix: true 25 | }) 26 | } 27 | 28 | export const getTransactionStatusDescription = (status: string, timestamp: Maybe = null) => { 29 | let description = TRANSACTION_STATUS_DESCRIPTION[status] 30 | 31 | if (timestamp) { 32 | description = description.replace('%t', formatTimestamp(timestamp)) 33 | } 34 | 35 | return description 36 | } 37 | 38 | export const getConfirmationsStatusDescription = (status: string, home: string, foreign: string, fromHome: boolean) => { 39 | const statusDescription = fromHome ? CONFIRMATIONS_STATUS_DESCRIPTION_HOME : CONFIRMATIONS_STATUS_DESCRIPTION 40 | 41 | return statusDescription[status] 42 | } 43 | -------------------------------------------------------------------------------- /alm/src/utils/signatures.ts: -------------------------------------------------------------------------------- 1 | import Web3 from 'web3' 2 | 3 | function strip0x(s: string) { 4 | return Web3.utils.isHexStrict(s) ? s.substr(2) : s 5 | } 6 | 7 | export interface Signature { 8 | v: string 9 | r: string 10 | s: string 11 | } 12 | 13 | export function signatureToVRS(rawSignature: string): Signature { 14 | const signature = strip0x(rawSignature) 15 | const v = signature.substr(64 * 2) 16 | const r = signature.substr(0, 32 * 2) 17 | const s = signature.substr(32 * 2, 32 * 2) 18 | return { v, r, s } 19 | } 20 | 21 | export function packSignatures(array: Array): string { 22 | const length = strip0x(Web3.utils.toHex(array.length)) 23 | const msgLength = length.length === 1 ? `0${length}` : length 24 | const [v, r, s] = array.reduce(([vs, rs, ss], { v, r, s }) => [vs + v, rs + r, ss + s], ['', '', '']) 25 | return `0x${msgLength}${v}${r}${s}` 26 | } 27 | -------------------------------------------------------------------------------- /alm/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "preserve" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /audit/chainsecurity/FT-AMB-6.0.0-and-OmniBridge-1.1.0-contracts-security-assessment-report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/audit/chainsecurity/FT-AMB-6.0.0-and-OmniBridge-1.1.0-contracts-security-assessment-report.pdf -------------------------------------------------------------------------------- /audit/chainsecurity/FT-OmniBridge-contracts-1.0.0-rc2-security-assessment-report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/audit/chainsecurity/FT-OmniBridge-contracts-1.0.0-rc2-security-assessment-report.pdf -------------------------------------------------------------------------------- /audit/peppersec/POA-Network-Token-bridge-security-assessment-report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/audit/peppersec/POA-Network-Token-bridge-security-assessment-report.pdf -------------------------------------------------------------------------------- /audit/quantstamp/POA-Network-Token-bridge-security-assessment-report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/audit/quantstamp/POA-Network-Token-bridge-security-assessment-report.pdf -------------------------------------------------------------------------------- /audit/quantstamp/POA-Network-TokenBridge-contracts-5.4.1-security-assessment-report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/audit/quantstamp/POA-Network-TokenBridge-contracts-5.4.1-security-assessment-report.pdf -------------------------------------------------------------------------------- /audit/smartdec/POA-Network-TokenBridge-Contracts-v2-3-2-Security-Assessment.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/audit/smartdec/POA-Network-TokenBridge-Contracts-v2-3-2-Security-Assessment.pdf -------------------------------------------------------------------------------- /burner-wallet-plugin/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: "@typescript-eslint/parser", // Specifies the ESLint parser 3 | extends: [ 4 | "plugin:react/recommended", 5 | "plugin:@typescript-eslint/recommended", // Uses the recommended rules from @typescript-eslint/eslint-plugin 6 | "../.eslintrc" 7 | ], 8 | parserOptions: { 9 | ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features 10 | sourceType: "module", // Allows for the use of imports 11 | ecmaFeatures: { 12 | jsx: true // Allows for the parsing of JSX 13 | } 14 | }, 15 | rules: { 16 | "@typescript-eslint/explicit-function-return-type": "off", 17 | "@typescript-eslint/no-explicit-any": "off", // Reduce the use of 'any' 18 | "@typescript-eslint/no-non-null-assertion": "off", 19 | "@typescript-eslint/no-var-requires": "off", 20 | "react/prop-types": "off", 21 | "@typescript-eslint/ban-ts-ignore": "off", 22 | "@typescript-eslint/member-delimiter-style": "off", 23 | "@typescript-eslint/indent": "off", 24 | "@typescript-eslint/explicit-member-accessibility": "off" 25 | }, 26 | settings: { 27 | react: { 28 | version: "detect", 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /burner-wallet-plugin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 as plugin-base 2 | 3 | WORKDIR /mono 4 | COPY package.json . 5 | RUN mkdir -p contracts/node_modules 6 | 7 | COPY burner-wallet-plugin/package.json ./burner-wallet-plugin/ 8 | COPY burner-wallet-plugin/lerna.json ./burner-wallet-plugin/ 9 | COPY burner-wallet-plugin/yarn.lock ./burner-wallet-plugin/ 10 | COPY burner-wallet-plugin/tsconfig.json ./burner-wallet-plugin/ 11 | COPY burner-wallet-plugin/tokenbridge-bw-exchange/package.json ./burner-wallet-plugin/tokenbridge-bw-exchange/ 12 | COPY burner-wallet-plugin/staging/package.json ./burner-wallet-plugin/staging/ 13 | COPY burner-wallet-plugin/testing/package.json ./burner-wallet-plugin/testing/ 14 | COPY yarn.lock . 15 | RUN yarn install --production --frozen-lockfile 16 | 17 | COPY ./burner-wallet-plugin/tokenbridge-bw-exchange ./burner-wallet-plugin/tokenbridge-bw-exchange 18 | RUN yarn build:plugin 19 | 20 | 21 | FROM plugin-base as testing 22 | COPY ./burner-wallet-plugin/testing ./burner-wallet-plugin/testing 23 | WORKDIR /mono/burner-wallet-plugin 24 | CMD ["yarn", "start-testing"] 25 | 26 | 27 | FROM plugin-base as staging 28 | COPY ./burner-wallet-plugin/staging ./burner-wallet-plugin/staging 29 | WORKDIR /mono/burner-wallet-plugin 30 | CMD ["yarn", "start-staging"] 31 | -------------------------------------------------------------------------------- /burner-wallet-plugin/docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '2.4' 3 | services: 4 | staging: 5 | build: 6 | context: .. 7 | dockerfile: burner-wallet-plugin/Dockerfile 8 | target: staging 9 | environment: 10 | - NODE_ENV=production 11 | testing: 12 | build: 13 | context: .. 14 | dockerfile: burner-wallet-plugin/Dockerfile 15 | target: testing 16 | environment: 17 | - NODE_ENV=production 18 | -------------------------------------------------------------------------------- /burner-wallet-plugin/lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "basic-wallet", 4 | "local-wallet", 5 | "tokenbridge-bw-exchange" 6 | ], 7 | "npmClient": "yarn", 8 | "useWorkspaces": true, 9 | "version": "independent" 10 | } 11 | -------------------------------------------------------------------------------- /burner-wallet-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "burner-wallet-plugin", 3 | "description": "Burner Wallet 2 plugin", 4 | "version": "1.0.0", 5 | "license": "GPL-3.0-only", 6 | "private": true, 7 | "scripts": { 8 | "install": "lerna bootstrap", 9 | "build": "lerna run --ignore testing --ignore staging build --stream", 10 | "lint": "eslint '*/**/*.{js,ts,tsx}' --ignore-path ../.eslintignore", 11 | "start-staging": "lerna run --scope staging start --stream", 12 | "start-testing": "lerna run --scope testing start --stream", 13 | "test": "lerna run --ignore testing --ignore staging test --stream" 14 | }, 15 | "workspaces": [ 16 | "staging", 17 | "testing", 18 | "tokenbridge-bw-exchange" 19 | ], 20 | "dependencies": { 21 | "@types/color": "3.0.0", 22 | "@typescript-eslint/eslint-plugin": "1.13.0", 23 | "@typescript-eslint/parser": "1.13.0", 24 | "eslint-plugin-react": "7.19.0", 25 | "lerna": "3.16.4", 26 | "typescript": "3.5.3" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /burner-wallet-plugin/publish.md: -------------------------------------------------------------------------------- 1 | ## Plugin Package Information 2 | 3 | The package to be published gets its configuration from `tokenbridge/burner-wallet-plugin/tokenbridge-bw-exchange/package.json` 4 | 5 | ```json 6 | { 7 | "name": "tokenbridge-bw-exchange", 8 | "version": "1.0.0", 9 | "main": "dist/index.js", 10 | "types": "dist/index.d.ts", 11 | "files": [ 12 | "/dist" 13 | ] 14 | } 15 | ``` 16 | 17 | - `name` is the name of how package will be available in npm. 18 | - `main` is entry point for the package 19 | - `types` is the entry point for typescript types 20 | - `files` is the list of files included when publishing the package. So we have to run `yarn build` first to 21 | generate the `dist` folder. 22 | 23 | ## Steps to publish to npm 24 | 25 | 1. Create account in https://www.npmjs.com/ 26 | 27 | 2. Go to `tokenbridge/burner-wallet-plugin/tokenbridge-bw-exchange/` 28 | 29 | 3. Run `yarn build`. Make sure it generates the `dist` folder 30 | 31 | 4. Update `version` in `tokenbridge/burner-wallet-plugin/tokenbridge-bw-exchange/package.json` 32 | 5. Run `yarn login` and fill login information if required. 33 | 6. Run `yarn publish --access public`. 34 | The prompt will ask for the new version, complete it with the version from `package.json` 35 | 36 | More information in https://classic.yarnpkg.com/en/docs/publishing-a-package/ 37 | -------------------------------------------------------------------------------- /burner-wallet-plugin/staging/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_INFURA_KEY= 2 | -------------------------------------------------------------------------------- /burner-wallet-plugin/staging/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "staging", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@burner-wallet/assets": "^1.1.10", 7 | "@burner-wallet/core": "^1.1.0", 8 | "@burner-wallet/exchange": "^1.1.4", 9 | "@burner-wallet/metamask-plugin": "^1.0.0", 10 | "@burner-wallet/modern-ui": "^1.0.7", 11 | "@poanet/tokenbridge-bw-exchange": "^1.0.0", 12 | "@types/node": "12.0.4", 13 | "@types/react": "*", 14 | "@types/react-dom": "16.8.4", 15 | "@types/react-router-dom": "^4.3.3", 16 | "react": "^16.8.6", 17 | "react-dom": "^16.8.6", 18 | "react-scripts": "3.0.1", 19 | "typescript": "3.5.1" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | }, 39 | "devDependencies": {} 40 | } 41 | -------------------------------------------------------------------------------- /burner-wallet-plugin/staging/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/burner-wallet-plugin/staging/public/favicon.ico -------------------------------------------------------------------------------- /burner-wallet-plugin/staging/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | React App 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /burner-wallet-plugin/staging/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /burner-wallet-plugin/staging/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import BurnerCore from '@burner-wallet/core' 4 | import { InjectedSigner, LocalSigner } from '@burner-wallet/core/signers' 5 | import { XDaiBridge } from '@burner-wallet/exchange' 6 | import { xdai } from '@burner-wallet/assets' 7 | import { InfuraGateway, InjectedGateway, XDaiGateway } from '@burner-wallet/core/gateways' 8 | import Exchange from '@burner-wallet/exchange' 9 | import ModernUI from '@burner-wallet/modern-ui' 10 | import { 11 | Etc, 12 | Wetc, 13 | Dai, 14 | qDai, 15 | MOON, 16 | xMOON, 17 | TokenBridgeGateway, 18 | WETCBridge, 19 | QDAIBridge, 20 | MOONBridge 21 | } from '@poanet/tokenbridge-bw-exchange' 22 | import MetamaskPlugin from '@burner-wallet/metamask-plugin' 23 | 24 | const core = new BurnerCore({ 25 | signers: [new InjectedSigner(), new LocalSigner()], 26 | gateways: [ 27 | new InjectedGateway(), 28 | new XDaiGateway(), 29 | new InfuraGateway(process.env.REACT_APP_INFURA_KEY), 30 | new TokenBridgeGateway() 31 | ], 32 | assets: [xdai, Wetc, Etc, Dai, qDai, MOON, xMOON] 33 | }) 34 | 35 | const exchange = new Exchange([new XDaiBridge(), new WETCBridge(), new QDAIBridge(), new MOONBridge()]) 36 | 37 | const BurnerWallet = () => 38 | 39 | ReactDOM.render(, document.getElementById('root')) 40 | -------------------------------------------------------------------------------- /burner-wallet-plugin/staging/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /burner-wallet-plugin/staging/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "jsx": "preserve" 21 | }, 22 | "include": [ 23 | "src" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /burner-wallet-plugin/testing/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_INFURA_KEY= 2 | #REACT_APP_PK=0x 3 | 4 | REACT_APP_MODE=AMB_NATIVE_TO_ERC677 5 | 6 | REACT_APP_HOME_TOKEN_NAME=sPOA 7 | REACT_APP_HOME_NETWORK=77 8 | REACT_APP_HOME_MEDIATOR_ADDRESS=0x867949C3F2f66D827Ed40847FaA7B3a369370e13 9 | REACT_APP_HOME_TOKEN_ADDRESS= 10 | 11 | REACT_APP_FOREIGN_TOKEN_NAME=ksPOA 12 | REACT_APP_FOREIGN_NETWORK=42 13 | REACT_APP_FOREIGN_MEDIATOR_ADDRESS=0x99FB1a25caeB9c3a5Bf132686E2fe5e27BC0e2dd 14 | REACT_APP_FOREIGN_TOKEN_ADDRESS=0xff94183659f549D6273349696d73686Ee1d2AC83 15 | -------------------------------------------------------------------------------- /burner-wallet-plugin/testing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testing", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@burner-wallet/assets": "^1.1.10", 7 | "@burner-wallet/core": "^1.1.0", 8 | "@burner-wallet/exchange": "^1.1.4", 9 | "@burner-wallet/metamask-plugin": "^1.0.0", 10 | "@burner-wallet/modern-ui": "^1.0.7", 11 | "@poanet/tokenbridge-bw-exchange": "^1.0.0", 12 | "@types/node": "12.0.4", 13 | "@types/react": "16.8.19", 14 | "@types/react-dom": "16.8.4", 15 | "@types/react-router-dom": "^4.3.3", 16 | "react": "^16.8.6", 17 | "react-dom": "^16.8.6", 18 | "react-scripts": "3.0.1", 19 | "typescript": "3.5.1" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | }, 39 | "devDependencies": { 40 | "axios": "^0.19.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /burner-wallet-plugin/testing/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/burner-wallet-plugin/testing/public/favicon.ico -------------------------------------------------------------------------------- /burner-wallet-plugin/testing/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | React App 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /burner-wallet-plugin/testing/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Burner Wallet", 3 | "name": "Burner Wallet", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /burner-wallet-plugin/testing/src/LocalhostGateway.ts: -------------------------------------------------------------------------------- 1 | import { Gateway } from '@burner-wallet/core/gateways' 2 | import Web3 from 'web3' 3 | 4 | export default class LocalhostGateway extends Gateway { 5 | private readonly providers: object 6 | private readonly providerStrings: { [id: string]: string } 7 | constructor() { 8 | super() 9 | this.providerStrings = { 10 | '111': 'http://localhost:8545', 11 | '1337': 'http://localhost:8546' 12 | } 13 | this.providers = {} 14 | } 15 | 16 | isAvailable() { 17 | return true 18 | } 19 | 20 | getNetworks() { 21 | return ['111', '1337'] 22 | } 23 | 24 | _provider(network) { 25 | if (!this.providers[network]) { 26 | this._makeProvider(network) 27 | } 28 | return this.providers[network] 29 | } 30 | 31 | _makeProvider(network) { 32 | if (!this.providerStrings[network]) { 33 | throw new Error(`Network ${network} not supported by LocalhostGateway`) 34 | } 35 | 36 | this.providers[network] = new Web3.providers.HttpProvider(this.providerStrings[network]) 37 | } 38 | 39 | send(network, payload) { 40 | return new Promise((resolve, reject) => { 41 | if (this.getNetworks().indexOf(network) === -1) { 42 | return reject(new Error('LocalhostGateway does not support this network')) 43 | } 44 | 45 | this._provider(network).send(payload, (err, response) => { 46 | if (err) { 47 | return reject(err) 48 | } 49 | return resolve(response.result) 50 | }) 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /burner-wallet-plugin/testing/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /burner-wallet-plugin/testing/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "module": "esnext", 16 | "moduleResolution": "node", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "noEmit": true, 20 | "noImplicitAny": false, 21 | "jsx": "preserve" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@poanet/tokenbridge-bw-exchange", 3 | "version": "1.1.0", 4 | "license": "GPL-3.0", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "files": [ 8 | "/dist", 9 | "README.md" 10 | ], 11 | "scripts": { 12 | "build": "tsc", 13 | "start-basic": "tsc -w", 14 | "start-local": "tsc -w", 15 | "test": "TS_NODE_PROJECT=\"tsconfig.testing.json\" mocha -r ts-node/register test/**/*.spec.ts" 16 | }, 17 | "dependencies": { 18 | "@burner-wallet/assets": "^1.1.10", 19 | "@burner-wallet/core": "^1.1.9", 20 | "@burner-wallet/exchange": "^1.1.4", 21 | "@burner-wallet/types": "^1.0.6" 22 | }, 23 | "devDependencies": { 24 | "@types/mocha": "^7.0.2", 25 | "chai": "^4.2.0", 26 | "mocha": "^5.2.0", 27 | "ts-node": "^8.8.2", 28 | "typescript": "^3.5.2" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "https://github.com/poanetwork/tokenbridge.git", 33 | "directory": "burner-wallet-plugin/tokenbridge-bw-exchange" 34 | }, 35 | "homepage": "https://tokenbridge.net/", 36 | "keywords": [ 37 | "tokenbridge", 38 | "burner-wallet" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/bridges/MOONBridge.ts: -------------------------------------------------------------------------------- 1 | import { Mediator } from '../burner-wallet' 2 | 3 | export default class MOONBridge extends Mediator { 4 | constructor() { 5 | super({ 6 | assetA: 'xmoon', 7 | assetABridge: '0x1E0507046130c31DEb20EC2f870ad070Ff266079', 8 | assetB: 'moon', 9 | assetBBridge: '0xFEaB457D95D9990b7eb6c943c839258245541754' 10 | }) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/bridges/QDAIBridge.ts: -------------------------------------------------------------------------------- 1 | import { MediatorErcToNative } from '../burner-wallet' 2 | 3 | export default class QDAIBridge extends MediatorErcToNative { 4 | constructor() { 5 | super({ 6 | assetA: 'qdai', 7 | assetABridge: '0xFEaB457D95D9990b7eb6c943c839258245541754', 8 | assetB: 'dai', 9 | assetBBridge: '0xf6edFA16926f30b0520099028A145F4E06FD54ed' 10 | }) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/bridges/index.ts: -------------------------------------------------------------------------------- 1 | export { default as WETCBridge } from './WETCBridge' 2 | export { default as QDAIBridge } from './QDAIBridge' 3 | export { default as MOONBridge } from './MOONBridge' 4 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/assets/Dai.ts: -------------------------------------------------------------------------------- 1 | import BridgeableERC20Asset from './BridgeableERC20Asset' 2 | 3 | export default new BridgeableERC20Asset({ 4 | id: 'dai', 5 | name: 'Dai', 6 | network: '1', 7 | address: '0x6b175474e89094c44da98b954eedeac495271d0f', 8 | usdPrice: 1, 9 | icon: 'https://static.burnerfactory.com/icons/mcd.svg', 10 | bridgeModes: ['erc-to-native-amb'] 11 | }) 12 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/assets/MOON.ts: -------------------------------------------------------------------------------- 1 | import BridgeableERC20Asset from './BridgeableERC20Asset' 2 | 3 | export default new BridgeableERC20Asset({ 4 | id: 'moon', 5 | name: 'MOON', 6 | network: '4', 7 | address: '0xDF82c9014F127243CE1305DFE54151647d74B27A', 8 | icon: 'https://blockscout.com/poa/xdai/images/icons/moon.png', 9 | bridgeModes: ['erc-to-erc-amb'] 10 | }) 11 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/assets/Wetc.ts: -------------------------------------------------------------------------------- 1 | import { default as ERC677Asset } from './ERC677Asset' 2 | 3 | export default new ERC677Asset({ 4 | id: 'wetc', 5 | name: 'WETC', 6 | network: '1', 7 | address: '0x86aabcc646f290b9fc9bd05ce17c3858d1511da1' 8 | }) 9 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/assets/qDai.ts: -------------------------------------------------------------------------------- 1 | import NativeMediatorAsset from './NativeMediatorAsset' 2 | 3 | export default new NativeMediatorAsset({ 4 | id: 'qdai', 5 | name: 'qDai', 6 | network: '181', 7 | usdPrice: 1, 8 | mediatorAddress: '0xFEaB457D95D9990b7eb6c943c839258245541754' 9 | }) 10 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/assets/sPOA.ts: -------------------------------------------------------------------------------- 1 | import NativeMediatorAsset from './NativeMediatorAsset' 2 | 3 | export default new NativeMediatorAsset({ 4 | id: 'spoa', 5 | name: 'sPOA', 6 | network: '77' 7 | }) 8 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/assets/xMOON.ts: -------------------------------------------------------------------------------- 1 | import { default as ERC677Asset } from './ERC677Asset' 2 | 3 | export default new ERC677Asset({ 4 | id: 'xmoon', 5 | name: 'xMOON', 6 | network: '100', 7 | address: '0x1e16aa4Df73d29C029d94CeDa3e3114EC191E25A', 8 | icon: 'https://blockscout.com/poa/xdai/images/icons/moon.png' 9 | }) 10 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/gateways/TokenBridgeGateway.ts: -------------------------------------------------------------------------------- 1 | import { Gateway } from '@burner-wallet/core/gateways' 2 | import Web3 from 'web3' 3 | 4 | export default class TokenBridgeGateway extends Gateway { 5 | private readonly providers: object 6 | private readonly providerStrings: { [id: string]: string } 7 | constructor() { 8 | super() 9 | this.providerStrings = { 10 | '61': `https://www.ethercluster.com/etc`, 11 | '77': 'https://sokol.poa.network', 12 | '99': 'https://core.poa.network', 13 | '181': 'https://quorum-rpc.tokenbridge.net' 14 | } 15 | this.providers = {} 16 | } 17 | 18 | isAvailable() { 19 | return true 20 | } 21 | 22 | getNetworks() { 23 | return ['61', '77', '99', '181'] 24 | } 25 | 26 | _provider(network) { 27 | if (!this.providers[network]) { 28 | this._makeProvider(network) 29 | } 30 | return this.providers[network] 31 | } 32 | 33 | _makeProvider(network) { 34 | if (!this.providerStrings[network]) { 35 | throw new Error(`Network ${network} not supported by TokenBridgeGateway`) 36 | } 37 | 38 | this.providers[network] = new Web3.providers.HttpProvider(this.providerStrings[network]) 39 | } 40 | 41 | send(network, payload) { 42 | return new Promise((resolve, reject) => { 43 | if (this.getNetworks().indexOf(network) === -1) { 44 | return reject(new Error('TokenBridgeGateway does not support this network')) 45 | } 46 | 47 | this._provider(network).send(payload, (err, response) => { 48 | if (err) { 49 | return reject(err) 50 | } 51 | return resolve(response.result) 52 | }) 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/burner-wallet/index.ts: -------------------------------------------------------------------------------- 1 | export { default as sPOA } from './assets/sPOA' 2 | export { default as Etc } from './assets/Etc' 3 | export { default as Wetc } from './assets/Wetc' 4 | export { default as Dai } from './assets/Dai' 5 | export { default as qDai } from './assets/qDai' 6 | export { default as MOON } from './assets/MOON' 7 | export { default as xMOON } from './assets/xMOON' 8 | export { default as ERC677Asset } from './assets/ERC677Asset' 9 | export { default as BridgeableERC20Asset } from './assets/BridgeableERC20Asset' 10 | export { default as NativeMediatorAsset } from './assets/NativeMediatorAsset' 11 | export { default as TokenBridgeGateway } from './gateways/TokenBridgeGateway' 12 | export { default as Mediator } from './pairs/Mediator' 13 | export { default as MediatorErcToNative } from './pairs/MediatorErcToNative' 14 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | ERC677Asset, 3 | BridgeableERC20Asset, 4 | NativeMediatorAsset, 5 | sPOA, 6 | Etc, 7 | Wetc, 8 | qDai, 9 | Dai, 10 | MOON, 11 | xMOON, 12 | TokenBridgeGateway, 13 | Mediator, 14 | MediatorErcToNative 15 | } from './burner-wallet' 16 | export { WETCBridge, QDAIBridge, MOONBridge } from './bridges' 17 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/utils/abis.ts: -------------------------------------------------------------------------------- 1 | export { default as ERC677_ABI } from './abis/ERC677' 2 | export { default as FOREIGN_NATIVE_TO_ERC_ABI } from './abis/ForeignBridgeNativeToErc' 3 | export { default as HOME_NATIVE_TO_ERC_ABI } from './abis/HomeBridgeNativeToErc' 4 | export { default as MEDIATOR_ABI } from './abis/Mediator' 5 | export { default as MEDIATOR_FEE_MANAGER_ABI } from './abis/MediatorFeeManager' 6 | export { default as MEDIATOR_ERC_TO_NATIVE_ABI } from './abis/MediatorErcToNative' 7 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/utils/abis/ForeignBridgeNativeToErc.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | constant: true, 4 | inputs: [ 5 | { 6 | name: '_txHash', 7 | type: 'bytes32' 8 | } 9 | ], 10 | name: 'relayedMessages', 11 | outputs: [ 12 | { 13 | name: '', 14 | type: 'bool' 15 | } 16 | ], 17 | payable: false, 18 | stateMutability: 'view', 19 | type: 'function' 20 | }, 21 | { 22 | constant: true, 23 | inputs: [], 24 | name: 'deployedAtBlock', 25 | outputs: [ 26 | { 27 | name: '', 28 | type: 'uint256' 29 | } 30 | ], 31 | payable: false, 32 | stateMutability: 'view', 33 | type: 'function' 34 | }, 35 | { 36 | constant: true, 37 | inputs: [], 38 | name: 'getHomeFee', 39 | outputs: [ 40 | { 41 | name: '', 42 | type: 'uint256' 43 | } 44 | ], 45 | payable: false, 46 | stateMutability: 'view', 47 | type: 'function' 48 | } 49 | ] 50 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/utils/abis/HomeBridgeNativeToErc.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | anonymous: false, 4 | inputs: [ 5 | { 6 | indexed: false, 7 | name: 'recipient', 8 | type: 'address' 9 | }, 10 | { 11 | indexed: false, 12 | name: 'value', 13 | type: 'uint256' 14 | }, 15 | { 16 | indexed: false, 17 | name: 'transactionHash', 18 | type: 'bytes32' 19 | } 20 | ], 21 | name: 'AffirmationCompleted', 22 | type: 'event' 23 | }, 24 | { 25 | constant: true, 26 | inputs: [], 27 | name: 'deployedAtBlock', 28 | outputs: [ 29 | { 30 | name: '', 31 | type: 'uint256' 32 | } 33 | ], 34 | payable: false, 35 | stateMutability: 'view', 36 | type: 'function' 37 | }, 38 | { 39 | constant: true, 40 | inputs: [], 41 | name: 'getForeignFee', 42 | outputs: [ 43 | { 44 | name: '', 45 | type: 'uint256' 46 | } 47 | ], 48 | payable: false, 49 | stateMutability: 'view', 50 | type: 'function' 51 | } 52 | ] 53 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/utils/abis/Mediator.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | anonymous: false, 4 | inputs: [ 5 | { 6 | indexed: true, 7 | name: 'recipient', 8 | type: 'address' 9 | }, 10 | { 11 | indexed: false, 12 | name: 'value', 13 | type: 'uint256' 14 | }, 15 | { 16 | indexed: true, 17 | name: 'messageId', 18 | type: 'bytes32' 19 | } 20 | ], 21 | name: 'TokensBridged', 22 | type: 'event' 23 | }, 24 | { 25 | constant: true, 26 | inputs: [], 27 | name: 'feeManagerContract', 28 | outputs: [ 29 | { 30 | name: '', 31 | type: 'address' 32 | } 33 | ], 34 | payable: false, 35 | stateMutability: 'view', 36 | type: 'function' 37 | }, 38 | { 39 | constant: false, 40 | inputs: [ 41 | { 42 | name: '', 43 | type: 'address' 44 | }, 45 | { 46 | name: '', 47 | type: 'uint256' 48 | } 49 | ], 50 | name: 'relayTokens', 51 | outputs: [], 52 | payable: false, 53 | stateMutability: 'nonpayable', 54 | type: 'function' 55 | }, 56 | { 57 | constant: true, 58 | inputs: [], 59 | name: 'getBridgeMode', 60 | outputs: [ 61 | { 62 | name: '', 63 | type: 'bytes4' 64 | } 65 | ], 66 | payable: false, 67 | stateMutability: 'pure', 68 | type: 'function' 69 | } 70 | ] 71 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/utils/abis/MediatorErcToNative.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | constant: true, 4 | inputs: [ 5 | { 6 | name: '', 7 | type: 'bytes32' 8 | } 9 | ], 10 | name: 'getFee', 11 | outputs: [ 12 | { 13 | name: '', 14 | type: 'uint256' 15 | } 16 | ], 17 | payable: false, 18 | stateMutability: 'view', 19 | type: 'function' 20 | }, 21 | { 22 | constant: true, 23 | inputs: [ 24 | { 25 | name: '', 26 | type: 'bytes32' 27 | }, 28 | { 29 | name: '', 30 | type: 'uint256' 31 | } 32 | ], 33 | name: 'calculateFee', 34 | outputs: [ 35 | { 36 | name: '', 37 | type: 'uint256' 38 | } 39 | ], 40 | payable: false, 41 | stateMutability: 'view', 42 | type: 'function' 43 | } 44 | ] 45 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/utils/abis/MediatorFeeManager.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | constant: true, 4 | inputs: [ 5 | { 6 | name: '', 7 | type: 'uint256' 8 | } 9 | ], 10 | name: 'calculateFee', 11 | outputs: [ 12 | { 13 | name: '', 14 | type: 'uint256' 15 | } 16 | ], 17 | payable: false, 18 | stateMutability: 'view', 19 | type: 'function' 20 | }, 21 | { 22 | constant: true, 23 | inputs: [], 24 | name: 'fee', 25 | outputs: [ 26 | { 27 | name: '', 28 | type: 'uint256' 29 | } 30 | ], 31 | payable: false, 32 | stateMutability: 'view', 33 | type: 'function' 34 | } 35 | ] 36 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export { constants, wait, waitForEvent, isVanillaBridgeContract, isBridgeContract } from './utils' 2 | export { 3 | ERC677_ABI, 4 | FOREIGN_NATIVE_TO_ERC_ABI, 5 | HOME_NATIVE_TO_ERC_ABI, 6 | MEDIATOR_ABI, 7 | MEDIATOR_FEE_MANAGER_ABI, 8 | MEDIATOR_ERC_TO_NATIVE_ABI 9 | } from './abis' 10 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "src", 5 | "outDir": "./dist" 6 | }, 7 | "include": [ 8 | "./src" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tokenbridge-bw-exchange/tsconfig.testing.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "commonjs" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /burner-wallet-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "esnext", 4 | "declaration": true, 5 | "removeComments": true, 6 | "noLib": false, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "esModuleInterop": true, 10 | "jsx": "react", 11 | "target": "esnext", 12 | "sourceMap": true, 13 | "strict": true, 14 | "allowSyntheticDefaultImports": true, 15 | "moduleResolution": "node", 16 | "resolveJsonModule": true, 17 | "noImplicitAny": false, 18 | "lib": [ 19 | "es6", 20 | "dom" 21 | ], 22 | "types" : [ 23 | "node" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "**/*.spec.ts" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /commons/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb-base", 4 | "../.eslintrc" 5 | ], 6 | "rules": { 7 | "no-unused-expressions": "off", 8 | "import/no-extraneous-dependencies": "off", 9 | "no-bitwise": "off" 10 | }, 11 | "env": { 12 | "mocha": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /commons/README.md: -------------------------------------------------------------------------------- 1 | # POA TokenBridge / Commons 2 | Interfaces, constants and utilities shared between the sub-repositories 3 | -------------------------------------------------------------------------------- /commons/constants.js: -------------------------------------------------------------------------------- 1 | const BRIDGE_MODES = { 2 | ERC_TO_NATIVE: 'ERC_TO_NATIVE', 3 | ARBITRARY_MESSAGE: 'ARBITRARY_MESSAGE', 4 | AMB_ERC_TO_ERC: 'AMB_ERC_TO_ERC' 5 | } 6 | 7 | const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' 8 | 9 | module.exports = { 10 | BRIDGE_MODES, 11 | ZERO_ADDRESS 12 | } 13 | -------------------------------------------------------------------------------- /commons/index.js: -------------------------------------------------------------------------------- 1 | const constants = require('./constants') 2 | const abis = require('./abis') 3 | const utils = require('./utils') 4 | const message = require('./message') 5 | 6 | module.exports = { 7 | ...constants, 8 | ...abis, 9 | ...utils, 10 | ...message 11 | } 12 | -------------------------------------------------------------------------------- /commons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "commons", 3 | "version": "0.0.1", 4 | "private": true, 5 | "main": "index.js", 6 | "scripts": { 7 | "lint": "eslint . --ignore-path ../.eslintignore", 8 | "test": "NODE_ENV=test mocha" 9 | }, 10 | "dependencies": { 11 | "@mycrypto/gas-estimation": "^1.1.0", 12 | "gas-price-oracle": "^0.1.5", 13 | "web3-utils": "^1.3.0", 14 | "node-fetch": "^2.1.2" 15 | }, 16 | "devDependencies": { 17 | "bn-chai": "^1.0.1", 18 | "chai": "^4.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /commons/test/constants.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require('chai') 2 | const { BRIDGE_MODES } = require('../constants') 3 | 4 | describe('constants', () => { 5 | it('should contain correct number of bridge types', () => { 6 | expect(Object.keys(BRIDGE_MODES).length).to.be.equal(3) 7 | }) 8 | }) 9 | -------------------------------------------------------------------------------- /commons/test/message.test.js: -------------------------------------------------------------------------------- 1 | const { BN } = require('web3-utils') 2 | const { expect } = require('chai').use(require('bn-chai')(BN)) 3 | const { parseAMBMessage, strip0x } = require('../message') 4 | 5 | describe('strip0x', () => { 6 | it('should remove 0x from input', () => { 7 | // Given 8 | const input = '0x12345' 9 | 10 | // When 11 | const result = strip0x(input) 12 | 13 | // Then 14 | expect(result).to.be.equal('12345') 15 | }) 16 | it('should not modify input if 0x is not present', () => { 17 | // Given 18 | const input = '12345' 19 | 20 | // When 21 | const result = strip0x(input) 22 | 23 | // Then 24 | expect(result).to.be.equal(input) 25 | }) 26 | }) 27 | describe('parseAMBMessage', () => { 28 | it('should parse data type 00', () => { 29 | const msgSender = '0x003667154bb32e42bb9e1e6532f19d187fa0082e' 30 | const msgExecutor = '0xf4bef13f9f4f2b203faf0c3cbbaabe1afe056955' 31 | const msgId = '0xbdceda9d8c94838aca10c687da1411a07b1390e88239c0638cb9cc264219cc10' 32 | const msgGasLimit = '000000000000000000000000000000000000000000000000000000005b877705' 33 | const msgDataType = '00' 34 | const msgData = '0xb1591967aed668a4b27645ff40c444892d91bf5951b382995d4d4f6ee3a2ce03' 35 | const message = `0x${strip0x(msgId)}${strip0x(msgSender)}${strip0x( 36 | msgExecutor 37 | )}${msgGasLimit}${msgDataType}${strip0x(msgData)}` 38 | 39 | // when 40 | const { sender, executor, messageId } = parseAMBMessage(message) 41 | 42 | // then 43 | expect(sender).to.be.equal(msgSender) 44 | expect(executor).to.be.equal(msgExecutor) 45 | expect(messageId).to.be.equal(msgId) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /deployment-e2e/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-stretch 2 | RUN curl -fsSL https://get.docker.com | sh 3 | RUN pip3 install docker molecule==2.22rc1 molecule[docker] flake8 4 | WORKDIR mono/deployment-e2e 5 | -------------------------------------------------------------------------------- /deployment-e2e/README.md: -------------------------------------------------------------------------------- 1 | # POA TokenBridge / Deployment Testing 2 | 3 | The deployment playbooks are tested using [Molecule](https://molecule.readthedocs.io). 4 | 5 | ## Push remote branch 6 | 7 | The deployment playbooks are cloning the monorepository on target hosts, using your current local git branch name. If the branch does not exists on remote, you need to push it. 8 | 9 | ``` 10 | git push 11 | ``` 12 | 13 | Alternatively, if there are no changes except the playbooks, you can use the `master` branch: 14 | 15 | ``` 16 | ./molecule.sh 17 | ``` 18 | 19 | In this case `master` branch will be used as a codebase for Monitor, Oracle and Contracts deployed by your local playbook. 20 | 21 | ## Run the tests 22 | 23 | ``` 24 | ./molecule.sh 25 | ``` 26 | 27 | Available scenarios: 28 | 29 | Scenario | Description 30 | --- | --- 31 | oracle | Deploys and checks standalone Oracle on Ubuntu host 32 | 33 | ## Ultimate E2E tests 34 | 35 | For information on the Ultimate tests, please refer to [Ultimate](../e2e-commons/ULTIMATE.md). 36 | -------------------------------------------------------------------------------- /deployment-e2e/molecule.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd ./e2e-commons 3 | set -e # exit when any command fails 4 | 5 | if [ -z "$CI" ]; then 6 | docker-compose build molecule_runner 7 | else 8 | docker-compose pull molecule_runner 9 | fi 10 | docker network create --driver bridge ultimate || true 11 | while [ "$1" != "" ]; do 12 | docker-compose run molecule_runner /bin/bash -c "molecule test --scenario-name $1" 13 | 14 | shift # Shift all the parameters down by one 15 | done 16 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/monitor/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # Molecule managed 2 | 3 | {% if item.registry is defined %} 4 | FROM {{ item.registry.url }}/{{ item.image }} 5 | {% else %} 6 | FROM {{ item.image }} 7 | {% endif %} 8 | 9 | RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ 10 | elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \ 11 | 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; \ 12 | elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \ 13 | elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ 14 | elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi 15 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/monitor/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - import_playbook: ../../../deployment/site.yml 3 | - import_playbook: ./run-checks.yml 4 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/monitor/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: docker 6 | lint: 7 | name: yamllint 8 | enabled: True 9 | options: 10 | config-data: 11 | ignore: ../../hosts.yml 12 | platforms: 13 | - name: monitor-host 14 | groups: 15 | - example 16 | children: 17 | - monitor 18 | image: ubuntu:16.04 19 | privileged: true 20 | network_mode: host 21 | volumes: 22 | - /var/run/docker.sock:/var/run/docker.sock 23 | provisioner: 24 | name: ansible 25 | lint: 26 | name: ansible-lint 27 | enabled: True 28 | options: 29 | r: ["bug"] 30 | playbooks: 31 | prepare: ../prepare.yml 32 | converge: ./converge.yml 33 | inventory: 34 | host_vars: 35 | monitor-host: 36 | MONITOR_PORT: 3003 37 | syslog_server_port: "udp://127.0.0.1:514" 38 | verifier: 39 | name: testinfra 40 | lint: 41 | name: flake8 42 | additional_files_or_dirs: 43 | - ../../tests/* 44 | scenario: 45 | name: monitor 46 | test_sequence: 47 | - lint 48 | - cleanup 49 | - destroy 50 | - dependency 51 | - syntax 52 | - create 53 | - prepare 54 | - converge 55 | - verify 56 | - destroy 57 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/monitor/run-checks.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Generate initial data for monitor 3 | hosts: monitor 4 | become: true 5 | tasks: 6 | - name: Run monitor checks 7 | shell: /bin/bash -c 'cd /home/poadocker/bridge/monitor/scripts; ./getBridgeStats.sh >cronWorker.out 2>cronWorker.err' 8 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/monitor/tests/test_monitor.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import testinfra.utils.ansible_runner 4 | 5 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 6 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('monitor') 7 | 8 | 9 | @pytest.mark.parametrize("name", [ 10 | ("monitor_monitor_1") 11 | ]) 12 | def test_docker_containers(host, name): 13 | container = host.docker(name) 14 | assert container.is_running 15 | 16 | 17 | @pytest.mark.parametrize("service", [ 18 | ("tokenbridge-monitor"), 19 | ("rsyslog") 20 | ]) 21 | def test_services(host, service): 22 | assert host.service(service).is_enabled 23 | assert host.service(service).is_running 24 | 25 | 26 | @pytest.mark.parametrize("filename", [ 27 | ("/etc/rsyslog.d/33-monitor-docker.conf"), 28 | ("/etc/rsyslog.d/38-monitor-remote-logging.conf") 29 | ]) 30 | def test_logging(host, filename): 31 | assert host.file(filename).exists 32 | assert host.file(filename).mode == 0o0644 33 | 34 | 35 | def test_home_exists(host): 36 | assert host.run_test( 37 | 'curl -s http://localhost:3003/bridge | ' 38 | 'grep -q -i "home"' 39 | ) 40 | 41 | 42 | def test_foreign_exists(host): 43 | assert host.run_test( 44 | 'curl -s http://localhost:3003/bridge | ' 45 | 'grep -q -i "foreign"' 46 | ) 47 | 48 | 49 | def test_no_error(host): 50 | assert host.run_expect( 51 | [1], 52 | 'curl -s http://localhost:3003/bridge | ' 53 | 'grep -i -q "error"' 54 | ) 55 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/multiple/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # Molecule managed 2 | 3 | {% if item.registry is defined %} 4 | FROM {{ item.registry.url }}/{{ item.image }} 5 | {% else %} 6 | FROM {{ item.image }} 7 | {% endif %} 8 | 9 | RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ 10 | elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \ 11 | 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; \ 12 | elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \ 13 | elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ 14 | elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi 15 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/multiple/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: docker 6 | lint: 7 | name: yamllint 8 | enabled: True 9 | options: 10 | config-data: 11 | ignore: ../../hosts.yml 12 | platforms: 13 | - name: multiple-host 14 | groups: 15 | - example 16 | children: 17 | - oracle 18 | - monitor 19 | image: ubuntu:16.04 20 | privileged: true 21 | network_mode: host 22 | volumes: 23 | - /var/run/docker.sock:/var/run/docker.sock 24 | provisioner: 25 | name: ansible 26 | lint: 27 | name: ansible-lint 28 | enabled: True 29 | options: 30 | r: ["bug"] 31 | playbooks: 32 | prepare: ../prepare.yml 33 | converge: ../monitor/converge.yml 34 | inventory: 35 | host_vars: 36 | multiple-host: 37 | ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "6c48435bd464a53ed66ed62127c4dba8af75cf1a99a8ebe2680599948fbfbc6d" 38 | MONITOR_PORT: 3003 39 | syslog_server_port: "udp://127.0.0.1:514" 40 | verifier: 41 | name: testinfra 42 | lint: 43 | name: flake8 44 | additional_files_or_dirs: 45 | - ../../tests/* 46 | scenario: 47 | name: multiple 48 | test_sequence: 49 | - lint 50 | - cleanup 51 | - destroy 52 | - dependency 53 | - syntax 54 | - create 55 | - prepare 56 | - converge 57 | - verify 58 | - destroy 59 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/multiple/tests/test_multiple.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import testinfra.utils.ansible_runner 4 | 5 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 6 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 7 | 8 | 9 | @pytest.mark.parametrize("service", [ 10 | ("poabridge"), 11 | ("tokenbridge-monitor") 12 | ]) 13 | def test_services(host, service): 14 | assert host.service(service).is_enabled 15 | assert host.service(service).is_running 16 | 17 | 18 | @pytest.mark.parametrize("name", [ 19 | ("oracle_rabbit_1"), 20 | ("oracle_redis_1"), 21 | ("oracle_bridge_request_1"), 22 | ("oracle_bridge_collected_1"), 23 | ("oracle_bridge_affirmation_1"), 24 | ("oracle_bridge_senderhome_1"), 25 | ("oracle_bridge_senderforeign_1"), 26 | ("oracle_bridge_shutdown_1"), 27 | ("monitor_monitor_1") 28 | ]) 29 | def test_docker_containers(host, name): 30 | container = host.docker(name) 31 | assert container.is_running 32 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/oracle/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # Molecule managed 2 | 3 | {% if item.registry is defined %} 4 | FROM {{ item.registry.url }}/{{ item.image }} 5 | {% else %} 6 | FROM {{ item.image }} 7 | {% endif %} 8 | 9 | RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ 10 | elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \ 11 | 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; \ 12 | elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \ 13 | elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ 14 | elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi 15 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/oracle/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependency: 3 | name: galaxy 4 | driver: 5 | name: docker 6 | lint: 7 | name: yamllint 8 | enabled: True 9 | options: 10 | config-data: 11 | ignore: ../../hosts.yml 12 | platforms: 13 | - name: oracle-host 14 | groups: 15 | - example 16 | children: 17 | - oracle 18 | image: ubuntu:16.04 19 | privileged: true 20 | network_mode: host 21 | volumes: 22 | - /var/run/docker.sock:/var/run/docker.sock 23 | provisioner: 24 | name: ansible 25 | lint: 26 | name: ansible-lint 27 | enabled: True 28 | options: 29 | r: ["bug"] 30 | playbooks: 31 | prepare: ../prepare.yml 32 | converge: ../../../deployment/site.yml 33 | inventory: 34 | host_vars: 35 | oracle-host: 36 | ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "6c48435bd464a53ed66ed62127c4dba8af75cf1a99a8ebe2680599948fbfbc6d" 37 | syslog_server_port: "udp://127.0.0.1:514" 38 | verifier: 39 | name: testinfra 40 | lint: 41 | name: flake8 42 | additional_files_or_dirs: 43 | - ../../tests/* 44 | scenario: 45 | name: oracle 46 | test_sequence: 47 | - lint 48 | - cleanup 49 | - destroy 50 | - dependency 51 | - syntax 52 | - create 53 | - prepare 54 | - converge 55 | - verify 56 | - destroy 57 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/oracle/tests/test_oracle.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import testinfra.utils.ansible_runner 4 | 5 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 6 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('oracle') 7 | 8 | 9 | @pytest.mark.parametrize("name", [ 10 | ("oracle_rabbit_1"), 11 | ("oracle_redis_1"), 12 | ("oracle_bridge_request_1"), 13 | ("oracle_bridge_collected_1"), 14 | ("oracle_bridge_affirmation_1"), 15 | ("oracle_bridge_senderhome_1"), 16 | ("oracle_bridge_senderforeign_1"), 17 | ("oracle_bridge_shutdown_1"), 18 | ]) 19 | def test_docker_containers(host, name): 20 | container = host.docker(name) 21 | assert container.is_running 22 | 23 | 24 | @pytest.mark.parametrize("service", [ 25 | ("poabridge"), 26 | ("rsyslog") 27 | ]) 28 | def test_services(host, service): 29 | assert host.service(service).is_enabled 30 | assert host.service(service).is_running 31 | 32 | 33 | @pytest.mark.parametrize("filename", [ 34 | ("/etc/rsyslog.d/31-oracle-docker.conf"), 35 | ("/etc/rsyslog.d/36-oracle-remote-logging.conf") 36 | ]) 37 | def test_logging(host, filename): 38 | assert host.file(filename).exists 39 | assert host.file(filename).mode == 0o0644 40 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/prepare.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: prepare 3 | hosts: all 4 | tasks: 5 | - name: install apt packages 6 | apt: 7 | name: "{{ packages }}" 8 | vars: 9 | packages: 10 | - apt-transport-https 11 | - rsyslog 12 | - shell: service rsyslog start 13 | - shell: groupadd docker && chgrp docker /var/run/docker.sock 14 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/repo/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # Molecule managed 2 | 3 | {% if item.registry is defined %} 4 | FROM {{ item.registry.url }}/{{ item.image }} 5 | {% else %} 6 | FROM {{ item.image }} 7 | {% endif %} 8 | 9 | RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ 10 | elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \ 11 | 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; \ 12 | elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \ 13 | elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ 14 | elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi 15 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/repo/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Repository 3 | hosts: all 4 | become: true 5 | tasks: 6 | - import_role: 7 | name: ../../../deployment/roles/common 8 | tasks_from: repo 9 | # Test that running the task again works 10 | - import_role: 11 | name: ../../../deployment/roles/common 12 | tasks_from: repo 13 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/repo/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: docker 4 | platforms: 5 | - name: repo-host 6 | image: ubuntu:16.04 7 | privileged: true 8 | network_mode: host 9 | volumes: 10 | - /var/run/docker.sock:/var/run/docker.sock 11 | provisioner: 12 | name: ansible 13 | playbooks: 14 | prepare: ../prepare.yml 15 | converge: ./converge.yml 16 | inventory: 17 | host_vars: 18 | repo-host: 19 | bridge_repo_branch: master 20 | verifier: 21 | name: testinfra 22 | scenario: 23 | name: repo 24 | test_sequence: 25 | - destroy 26 | - create 27 | - prepare 28 | - converge 29 | - verify 30 | - destroy 31 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/repo/tests/test_existing.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import testinfra.utils.ansible_runner 4 | 5 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 6 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 7 | 8 | 9 | @pytest.mark.parametrize("path", [ 10 | ("/home/poadocker"), 11 | ("/home/poadocker/bridge"), 12 | ("/home/poadocker/bridge/commons"), 13 | ("/home/poadocker/bridge/e2e-commons"), 14 | ("/home/poadocker/bridge/deployment"), 15 | ("/home/poadocker/bridge/contracts"), 16 | ("/home/poadocker/bridge/oracle"), 17 | ("/home/poadocker/bridge/monitor"), 18 | ("/home/poadocker/bridge/parity") 19 | ]) 20 | def test_existing_folders(host, path): 21 | assert host.file(path).exists 22 | assert host.file(path).is_directory 23 | 24 | 25 | @pytest.mark.parametrize("path", [ 26 | ("/home/poadocker/bridge/package.json"), 27 | ("/home/poadocker/bridge/commons/package.json"), 28 | ("/home/poadocker/bridge/contracts/package.json"), 29 | ("/home/poadocker/bridge/oracle/package.json"), 30 | ("/home/poadocker/bridge/monitor/package.json") 31 | ]) 32 | def test_existing_package_json(host, path): 33 | assert host.file(path).exists 34 | assert host.file(path).is_file 35 | 36 | 37 | @pytest.mark.parametrize("path", [ 38 | ("/home/poadocker/bridge/Dockerfile.e2e"), 39 | ("/home/poadocker/bridge/contracts/Dockerfile"), 40 | ("/home/poadocker/bridge/parity/Dockerfile"), 41 | ("/home/poadocker/bridge/oracle/Dockerfile"), 42 | ("/home/poadocker/bridge/monitor/Dockerfile") 43 | ]) 44 | def test_existing_docker_files(host, path): 45 | assert host.file(path).exists 46 | assert host.file(path).is_file 47 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/repo/tests/test_non_existing.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import testinfra.utils.ansible_runner 4 | 5 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 6 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 7 | 8 | 9 | @pytest.mark.parametrize("path", [ 10 | ("/home/poadocker/bridge/node_modules"), 11 | ("/home/poadocker/bridge/ui/node_modules"), 12 | ("/home/poadocker/bridge/oracle/node_modules"), 13 | ("/home/poadocker/bridge/monitor/node_modules"), 14 | ("/home/poadocker/bridge/contracts/node_modules"), 15 | ]) 16 | def test_non_existing_node_modules(host, path): 17 | assert not host.file(path).exists 18 | 19 | @pytest.mark.parametrize("path", [ 20 | ("/home/poadocker/bridge/.git") 21 | ]) 22 | def test_non_existing_git(host, path): 23 | assert not host.file(path).exists 24 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/tests/test_all.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import testinfra.utils.ansible_runner 4 | 5 | testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( 6 | os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all') 7 | 8 | 9 | def test_repo(host): 10 | assert host.file('/home/poadocker/bridge').exists 11 | assert host.file('/home/poadocker/bridge').is_directory 12 | 13 | 14 | def test_docker_group(host): 15 | assert host.group('docker').exists 16 | 17 | 18 | def test_user(host): 19 | assert host.user('poadocker').exists 20 | assert 'docker' in host.user('poadocker').groups 21 | 22 | 23 | @pytest.mark.parametrize("filename", [ 24 | ("/etc/rsyslog.d/30-docker.conf"), 25 | ("/etc/rsyslog.d/35-docker-remote-logging.conf") 26 | ]) 27 | def test_logging(host, filename): 28 | assert host.file(filename).exists 29 | assert host.file(filename).mode == 0o0644 30 | 31 | 32 | def test_docker_config(host): 33 | assert host.file('/etc/docker/daemon.json').exists 34 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/ultimate-amb/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # Molecule managed 2 | 3 | {% if item.registry is defined %} 4 | FROM {{ item.registry.url }}/{{ item.image }} 5 | {% else %} 6 | FROM {{ item.image }} 7 | {% endif %} 8 | 9 | RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ 10 | elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \ 11 | 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; \ 12 | elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \ 13 | elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ 14 | elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi 15 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/ultimate-amb/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: docker 4 | platforms: 5 | - name: oracle-amb-host 6 | groups: 7 | - ultimate 8 | - amb 9 | children: 10 | - oracle 11 | image: ubuntu:16.04 12 | privileged: true 13 | network_mode: host 14 | volumes: 15 | - /var/run/docker.sock:/var/run/docker.sock 16 | provisioner: 17 | name: ansible 18 | playbooks: 19 | prepare: ../prepare.yml 20 | converge: ../ultimate-commons/converge.yml 21 | inventory: 22 | host_vars: 23 | oracle-amb-host: 24 | ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" 25 | ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" 26 | verifier: 27 | name: testinfra 28 | lint: 29 | name: flake8 30 | scenario: 31 | name: ultimate-amb 32 | test_sequence: 33 | - cleanup 34 | - destroy 35 | - syntax 36 | - create 37 | - prepare 38 | - converge 39 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/ultimate-commons/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - import_playbook: ./oracle-docker-compose.yml 3 | - import_playbook: ../../../deployment/site.yml 4 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/ultimate-commons/oracle-docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Prepare Oracle for ultimate tests 3 | hosts: oracle 4 | become: true 5 | tasks: 6 | - name: Connect parity to oracle networks 7 | shell: "docker network create {{ item }} && docker network connect {{ item }} parity1 && docker network connect {{ item }} parity2" 8 | with_items: 9 | - oracle_net_db_bridge_request 10 | - oracle_net_db_bridge_collected 11 | - oracle_net_db_bridge_affirmation 12 | - oracle_net_db_bridge_information 13 | - oracle_net_db_bridge_transfer 14 | - oracle_net_db_bridge_senderhome 15 | - oracle_net_db_bridge_senderforeign 16 | - oracle_net_db_bridge_shutdown 17 | - oracle_net_rabbit_bridge_request 18 | - oracle_net_rabbit_bridge_collected 19 | - oracle_net_rabbit_bridge_affirmation 20 | - oracle_net_rabbit_bridge_information 21 | - oracle_net_rabbit_bridge_transfer 22 | - oracle_net_rabbit_bridge_senderhome 23 | - oracle_net_rabbit_bridge_senderforeign 24 | delegate_to: 127.0.0.1 25 | become: false 26 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/ultimate-erc-to-native/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # Molecule managed 2 | 3 | {% if item.registry is defined %} 4 | FROM {{ item.registry.url }}/{{ item.image }} 5 | {% else %} 6 | FROM {{ item.image }} 7 | {% endif %} 8 | 9 | RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ 10 | elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python sudo python-devel python*-dnf bash && dnf clean all; \ 11 | 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; \ 12 | elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \ 13 | elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ 14 | elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi 15 | -------------------------------------------------------------------------------- /deployment-e2e/molecule/ultimate-erc-to-native/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: docker 4 | platforms: 5 | - name: oracle-erc-to-native-host 6 | groups: 7 | - ultimate 8 | - erc-to-native 9 | children: 10 | - oracle 11 | image: ubuntu:16.04 12 | privileged: true 13 | network_mode: host 14 | volumes: 15 | - /var/run/docker.sock:/var/run/docker.sock 16 | provisioner: 17 | name: ansible 18 | playbooks: 19 | prepare: ../prepare.yml 20 | converge: ../ultimate-commons/converge.yml 21 | inventory: 22 | host_vars: 23 | oracle-erc-to-native-host: 24 | ORACLE_VALIDATOR_ADDRESS: "0xaaB52d66283F7A1D5978bcFcB55721ACB467384b" 25 | ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9" 26 | ORACLE_HOME_START_BLOCK: 1 27 | ORACLE_FOREIGN_START_BLOCK: 1 28 | verifier: 29 | name: testinfra 30 | lint: 31 | name: flake8 32 | scenario: 33 | name: ultimate-erc-to-native 34 | test_sequence: 35 | - cleanup 36 | - destroy 37 | - syntax 38 | - create 39 | - prepare 40 | - converge 41 | -------------------------------------------------------------------------------- /deployment/.yamllint: -------------------------------------------------------------------------------- 1 | extends: default 2 | 3 | rules: 4 | braces: 5 | max-spaces-inside: 1 6 | level: error 7 | brackets: 8 | max-spaces-inside: 1 9 | level: error 10 | line-length: disable 11 | truthy: disable 12 | -------------------------------------------------------------------------------- /deployment/README.md: -------------------------------------------------------------------------------- 1 | # POA TokenBridge / Deployment 2 | Ansible playbooks for deploying cross-chain bridges. 3 | 4 | ## Overview 5 | Please refer to the [POA TokenBridge](../README.md) overview first of all. 6 | 7 | These playbooks are designed to automate the deployment process for cross-chain bridges on bridge validator nodes. This process installs the bridge as a service and sets .env configurations on a remote server. 8 | 9 | ## Configuration 10 | 11 | Please refer to [Configuration](./CONFIGURATION.md). 12 | 13 | ## Execution 14 | 15 | Please refer to [Execution](./EXECUTION.md). 16 | 17 | ## Testing 18 | 19 | Please refer to [Deployment-E2E](../deployment-e2e/README.md). 20 | 21 | ## Contributing 22 | 23 | See the [CONTRIBUTING](../CONTRIBUTING.md) document for contribution, testing and pull request protocol. 24 | 25 | ## License 26 | 27 | [![License: LGPL v3.0](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0) 28 | 29 | This project is licensed under the GNU Lesser General Public License v3.0. See the [LICENSE](../LICENSE) file for details. 30 | 31 | 32 | -------------------------------------------------------------------------------- /deployment/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking = False 3 | stdout_callback = skippy 4 | ANSIBLE_DEBUG=1 5 | 6 | [ssh_connection] 7 | pipelining=True 8 | -------------------------------------------------------------------------------- /deployment/group_vars/amb.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ORACLE_BRIDGE_MODE: "ARBITRARY_MESSAGE" 3 | COMMON_HOME_BRIDGE_ADDRESS: "0x8397be90BCF57b0B71219f555Fe121b22e5a994C" 4 | COMMON_FOREIGN_BRIDGE_ADDRESS: "0x1feB40aD9420b186F019A717c37f5546165d411E" 5 | MONITOR_PORT: 3013 6 | -------------------------------------------------------------------------------- /deployment/group_vars/dai.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## General settings 3 | ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE" 4 | 5 | ## Home contract 6 | COMMON_HOME_RPC_URL: "https://dai.poa.network" 7 | COMMON_HOME_BRIDGE_ADDRESS: "0x7301CFA0e1756B71869E93d4e4Dca5c7d0eb0AA6" 8 | ORACLE_HOME_RPC_POLLING_INTERVAL: 5000 9 | 10 | ## Foreign contract 11 | COMMON_FOREIGN_RPC_URL: "https://mainnet.infura.io" 12 | COMMON_FOREIGN_BRIDGE_ADDRESS: "0x4aa42145Aa6Ebf72e164C9bBC74fbD3788045016" 13 | ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 5000 14 | 15 | ## Home Gasprice 16 | # COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://localhost:8888/" 17 | COMMON_HOME_GAS_PRICE_SPEED_TYPE: "standard" 18 | COMMON_HOME_GAS_PRICE_FALLBACK: 0 19 | COMMON_HOME_GAS_PRICE_FACTOR: 600000 20 | 21 | ## Foreign Gasprice 22 | COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/" 23 | COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard" 24 | COMMON_FOREIGN_GAS_PRICE_FALLBACK: 10000000000 25 | ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000 26 | COMMON_FOREIGN_GAS_PRICE_FACTOR: 1 27 | 28 | ## Monitor 29 | MONITOR_BRIDGE_NAME: "xdai" 30 | MONITOR_PORT: 3003 31 | MONITOR_CACHE_EVENTS: "true" 32 | MONITOR_HOME_START_BLOCK: 759 33 | MONITOR_FOREIGN_START_BLOCK: 6478417 34 | MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000 35 | MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000 36 | MONITOR_TX_NUMBER_THRESHOLD: 100 37 | -------------------------------------------------------------------------------- /deployment/group_vars/erc-to-native.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ORACLE_BRIDGE_MODE: "ERC_TO_NATIVE" 3 | COMMON_HOME_BRIDGE_ADDRESS: "0x5118AC62AE912Dd5B51EEfF7338c4fcb0248Ba8c" 4 | COMMON_FOREIGN_BRIDGE_ADDRESS: "0x32198D570fffC7033641F8A9094FFDCaAEF42624" 5 | MONITOR_PORT: 3012 6 | -------------------------------------------------------------------------------- /deployment/group_vars/example.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## General settings 3 | ORACLE_BRIDGE_MODE: "ARBITRARY_MESSAGE" 4 | ORACLE_LOG_LEVEL: debug 5 | 6 | ## Home contract 7 | COMMON_HOME_RPC_URL: "https://sokol.poa.network" 8 | COMMON_HOME_BRIDGE_ADDRESS: "0x59ba90A588ce732AB33FD32Aab1b58c21400A0f6" 9 | ORACLE_HOME_RPC_POLLING_INTERVAL: 5000 10 | 11 | ## Foreign contract 12 | COMMON_FOREIGN_RPC_URL: "https://kovan.infura.io/v3/5d7bd94c50ed43fab1cb8e74f58678b0" 13 | COMMON_FOREIGN_BRIDGE_ADDRESS: "0xdA4a49a00F4fF4A5988b9AceE95f99e3b2c208b6" 14 | ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 5000 15 | 16 | ## Home Gasprice 17 | COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/" 18 | COMMON_HOME_GAS_PRICE_SPEED_TYPE: "standard" 19 | COMMON_HOME_GAS_PRICE_FALLBACK: 1000000000 # in wei 20 | COMMON_HOME_GAS_PRICE_FACTOR: 1 21 | ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000 22 | 23 | ## Foreign Gasprice 24 | COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/" 25 | COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard" 26 | COMMON_FOREIGN_GAS_PRICE_FALLBACK: 1000000000 # in wei 27 | COMMON_FOREIGN_GAS_PRICE_FACTOR: 1 28 | ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000 29 | 30 | ## Monitor 31 | MONITOR_BRIDGE_NAME: "bridge" 32 | MONITOR_PORT: 3003 33 | MONITOR_CACHE_EVENTS: "false" 34 | MONITOR_HOME_START_BLOCK: 20821049 35 | MONITOR_FOREIGN_START_BLOCK: 24773297 36 | MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000 37 | MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000 38 | MONITOR_TX_NUMBER_THRESHOLD: 100 39 | -------------------------------------------------------------------------------- /deployment/group_vars/ultimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## General settings 3 | ORACLE_ALLOW_HTTP_FOR_RPC: yes 4 | ORACLE_LOG_LEVEL: debug 5 | 6 | ## Home contract 7 | COMMON_HOME_RPC_URL: "http://parity1:8545" 8 | ORACLE_HOME_RPC_POLLING_INTERVAL: 5000 9 | 10 | ## Foreign contract 11 | COMMON_FOREIGN_RPC_URL: "http://parity2:8545" 12 | ORACLE_FOREIGN_RPC_POLLING_INTERVAL: 1000 13 | 14 | ## Home Gasprice 15 | COMMON_HOME_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/" 16 | COMMON_HOME_GAS_PRICE_SPEED_TYPE: "standard" 17 | COMMON_HOME_GAS_PRICE_FALLBACK: 1000000000 # in wei 18 | COMMON_HOME_GAS_PRICE_FACTOR: 1 19 | ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL: 600000 20 | 21 | ## Foreign Gasprice 22 | COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL: "https://gasprice.poa.network/" 23 | COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE: "standard" 24 | COMMON_FOREIGN_GAS_PRICE_FALLBACK: 1000000000 # in wei 25 | COMMON_FOREIGN_GAS_PRICE_FACTOR: 1 26 | ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL: 600000 27 | 28 | #monitor 29 | MONITOR_BRIDGE_NAME: "bridge" 30 | MONITOR_CACHE_EVENTS: "true" 31 | MONITOR_HOME_START_BLOCK: 0 32 | MONITOR_FOREIGN_START_BLOCK: 0 33 | MONITOR_VALIDATOR_HOME_TX_LIMIT: 300000 34 | MONITOR_VALIDATOR_FOREIGN_TX_LIMIT: 300000 35 | MONITOR_TX_NUMBER_THRESHOLD: 100 36 | 37 | # disable building and pulling of docker images from the Docker Hub 38 | skip_pull: true 39 | skip_build: true 40 | -------------------------------------------------------------------------------- /deployment/hosts.yml.example: -------------------------------------------------------------------------------- 1 | --- 2 | sokol-kovan: 3 | children: 4 | oracle: 5 | hosts: 6 | 127.0.0.1: 7 | ansible_user: ubuntu 8 | ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 9 | #syslog_server_port: "udp://127.0.0.1:514" 10 | monitor: 11 | hosts: 12 | 127.0.0.1: 13 | ansible_user: ubuntu 14 | #syslog_server_port: "://:" 15 | #monitor_cron_schedule: "*/4 * * * *" 16 | -------------------------------------------------------------------------------- /deployment/requirements.txt: -------------------------------------------------------------------------------- 1 | # pre-release because it contains "CI Fixes for ansible 2.8" 2 | molecule==2.22rc1 3 | docker 4 | flake8 5 | -------------------------------------------------------------------------------- /deployment/roles/common/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | docker_compose_version: 1.23.2 3 | compose_service_user: poadocker 4 | bridge_path: "/home/{{ compose_service_user }}/bridge" 5 | -------------------------------------------------------------------------------- /deployment/roles/common/files/daemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "live-restore": true, 3 | "no-new-privileges": true 4 | } 5 | -------------------------------------------------------------------------------- /deployment/roles/common/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart rsyslog 3 | service: 4 | name: rsyslog 5 | state: restarted 6 | 7 | - name: restart auditd 8 | service: 9 | name: auditd 10 | state: restarted 11 | 12 | - name: restart docker 13 | service: 14 | name: docker 15 | state: restarted 16 | -------------------------------------------------------------------------------- /deployment/roles/common/tasks/logging.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Set the local docker logs configuration file 3 | template: 4 | src: 30-docker.conf.j2 5 | dest: /etc/rsyslog.d/30-docker.conf 6 | owner: root 7 | group: root 8 | mode: 0644 9 | 10 | - name: Set the log configuration file to send docker logs to remote server 11 | template: 12 | src: 35-docker-remote-logging.conf.j2 13 | dest: /etc/rsyslog.d/35-docker-remote-logging.conf 14 | owner: root 15 | group: root 16 | mode: 0644 17 | when: syslog_server_port is defined 18 | 19 | - name: Set the logrotate config file 20 | template: 21 | src: docker-logs.j2 22 | dest: /etc/logrotate.d/docker-logs 23 | owner: root 24 | group: root 25 | mode: 0644 26 | 27 | - name: restart rsyslog 28 | service: 29 | name: rsyslog 30 | state: restarted 31 | -------------------------------------------------------------------------------- /deployment/roles/common/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check if component is already deployed 3 | shell: "test -f {{ bridge_path }}/{{ component }}/.env && echo 'true'" 4 | ignore_errors: True 5 | register: already_deployed 6 | when: check_deployed is defined 7 | 8 | - name: Set if tasks should be skipped 9 | set_fact: skip_task="{{ already_deployed.stdout | default('false') }}" 10 | 11 | - name: Include dependencies tasks 12 | include_tasks: dependencies.yml 13 | when: skip_task != true 14 | 15 | - name: Include repo tasks 16 | include_tasks: repo.yml 17 | when: skip_task != true and skip_repo is undefined 18 | 19 | - name: Include logging tasks 20 | include_tasks: logging.yml 21 | when: skip_task != true 22 | -------------------------------------------------------------------------------- /deployment/roles/common/tasks/repo.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create archives of the monorepository 3 | shell: | 4 | (git ls-tree -r HEAD --name-only | sed '/^contracts$/d') | xargs tar zcfv monorepo.tar.gz --files-from - 5 | cd contracts; (git ls-tree -r HEAD --name-only) | xargs tar zcfv ../contracts.tar.gz --files-from - 6 | delegate_to: 127.0.0.1 7 | become: false 8 | args: 9 | chdir: "{{ lookup('env', 'PWD') }}/.." 10 | 11 | - name: Copy the archives 12 | copy: 13 | src: ../../../../{{ item }} 14 | dest: "{{ bridge_path }}/" 15 | mode: '0640' 16 | with_items: 17 | - monorepo.tar.gz 18 | - contracts.tar.gz 19 | 20 | - name: Untar the archives 21 | shell: | 22 | tar zxfv monorepo.tar.gz && rm monorepo.tar.gz 23 | mkdir -p contracts && tar zxfv contracts.tar.gz -C ./contracts && rm contracts.tar.gz 24 | args: 25 | chdir: "{{ bridge_path }}" 26 | 27 | - name: Remove local archives 28 | shell: rm {{ item }} 29 | delegate_to: 127.0.0.1 30 | become: false 31 | args: 32 | chdir: "{{ lookup('env', 'PWD') }}/.." 33 | with_items: 34 | - monorepo.tar.gz 35 | - contracts.tar.gz 36 | -------------------------------------------------------------------------------- /deployment/roles/common/templates/30-docker.conf.j2: -------------------------------------------------------------------------------- 1 | $FileCreateMode 0644 2 | 3 | if $programname startswith 'docker' then \ 4 | /var/log/docker/no_tag/docker.log 5 | 6 | $FileCreateMode 0600 7 | -------------------------------------------------------------------------------- /deployment/roles/common/templates/35-docker-remote-logging.conf.j2: -------------------------------------------------------------------------------- 1 | template(name="RemoteForwardFormat" type="list") { 2 | constant(value="<") 3 | property(name="pri") 4 | constant(value=">") 5 | property(name="timestamp" dateFormat="rfc3339") 6 | constant(value=" ") 7 | property(name="hostname") 8 | constant(value=" ") 9 | property(name="syslogtag") 10 | property(name="msg" spifno1stsp="on") 11 | property(name="msg") 12 | } 13 | 14 | if $programname startswith 'docker' then { 15 | action( 16 | type="omfwd" 17 | protocol="{{ syslog_server_port.split(":")[0] }}" 18 | target="{{ (syslog_server_port.split(":")[1])[2:] }}" 19 | port="{{ syslog_server_port.split(":")[2] }}" 20 | template="RemoteForwardFormat" 21 | queue.SpoolDirectory="/var/spool/rsyslog" 22 | queue.FileName="remote" 23 | queue.MaxDiskSpace="1g" 24 | queue.SaveOnShutdown="on" 25 | queue.Type="LinkedList" 26 | ResendLastMSGOnReconnect="on" 27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /deployment/roles/common/templates/docker-logs.j2: -------------------------------------------------------------------------------- 1 | /var/log/docker/*/docker.log { 2 | rotate 5 3 | size 100M 4 | compress 5 | missingok 6 | delaycompress 7 | copytruncate 8 | } 9 | /var/log/docker/*.log { 10 | rotate 5 11 | size 100M 12 | compress 13 | missingok 14 | delaycompress 15 | copytruncate 16 | } 17 | -------------------------------------------------------------------------------- /deployment/roles/monitor/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | monitor_cron_schedule: "*/4 * * * *" 3 | -------------------------------------------------------------------------------- /deployment/roles/monitor/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: common, skip_repo: true, check_deployed: true, component: 'monitor' } 4 | -------------------------------------------------------------------------------- /deployment/roles/monitor/tasks/cron.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Parse cron schedule 3 | set_fact: 4 | minute: "{{ monitor_cron_schedule.split(' ')[0] }}" 5 | hour: "{{ monitor_cron_schedule.split(' ')[1] }}" 6 | day: "{{ monitor_cron_schedule.split(' ')[2] }}" 7 | month: "{{ monitor_cron_schedule.split(' ')[3] }}" 8 | weekday: "{{ monitor_cron_schedule.split(' ')[4] }}" 9 | job: "/bin/bash -c 'cd {{ bridge_path }}/monitor/scripts; ./getBridgeStats.sh >cronWorker.out 2>cronWorker.err'" 10 | - name: Add cron entry 11 | cron: 12 | name: "RUN_MONITOR_CHECKS" 13 | minute: "{{ minute }}" 14 | hour: "{{ hour }}" 15 | day: "{{ day }}" 16 | month: "{{ month }}" 17 | weekday: "{{ weekday }}" 18 | job: "{{ job }}" 19 | -------------------------------------------------------------------------------- /deployment/roles/monitor/tasks/jumpbox.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Pull the containers images 3 | shell: docker-compose pull 4 | args: 5 | chdir: "{{ bridge_path }}/monitor" 6 | when: skip_pull is undefined 7 | -------------------------------------------------------------------------------- /deployment/roles/monitor/tasks/logging.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Slurp docker compose file 3 | slurp: 4 | src: "{{ bridge_path }}/monitor/docker-compose.yml" 5 | register: docker_compose_slurp 6 | 7 | - name: Parse docker compose file 8 | set_fact: 9 | docker_compose_parsed: "{{ docker_compose_slurp['content'] | b64decode | from_yaml }}" 10 | 11 | - name: Set logger to remote server 12 | set_fact: 13 | docker_compose_parsed: "{{ docker_compose_parsed |combine({'services': {item: {'logging': {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}}}}}, recursive=True) }}" 14 | with_items: "{{ docker_compose_parsed.services }}" 15 | 16 | - name: Write new docker-compose file 17 | copy: 18 | content: "{{ docker_compose_parsed | to_yaml }}" 19 | dest: "{{ bridge_path }}/monitor/docker-compose.yml" 20 | 21 | - name: Set the local container logs configuration file 22 | template: 23 | src: 33-monitor-docker.conf.j2 24 | dest: /etc/rsyslog.d/33-monitor-docker.conf 25 | owner: root 26 | group: root 27 | mode: 0644 28 | 29 | - name: Set the log configuration file to send container logs to remote server 30 | template: 31 | src: 38-monitor-remote-logging.conf.j2 32 | dest: /etc/rsyslog.d/38-monitor-remote-logging.conf 33 | owner: root 34 | group: root 35 | mode: 0644 36 | when: syslog_server_port is defined 37 | 38 | - name: restart rsyslog 39 | service: 40 | name: rsyslog 41 | state: restarted 42 | -------------------------------------------------------------------------------- /deployment/roles/monitor/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: pre_config.yml 3 | 4 | - name: Include logging tasks 5 | include_tasks: logging.yml 6 | when: skip_task != true 7 | 8 | - name: Include jumpbox tasks 9 | include_tasks: jumpbox.yml 10 | when: skip_task != true 11 | 12 | - name: Include servinstall tasks 13 | include_tasks: servinstall.yml 14 | when: skip_task != true 15 | 16 | - name: Include cron tasks 17 | include_tasks: cron.yml 18 | when: skip_task != true 19 | -------------------------------------------------------------------------------- /deployment/roles/monitor/tasks/pre_config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create configs directory 3 | file: 4 | path: "{{ bridge_path }}/monitor/configs" 5 | state: directory 6 | mode: '0755' 7 | when: skip_task != true 8 | 9 | - name: Create responses directory 10 | file: 11 | path: "{{ bridge_path }}/monitor/responses" 12 | state: directory 13 | mode: '0755' 14 | when: skip_task != true 15 | 16 | - name: Create scripts directory 17 | file: 18 | path: "{{ bridge_path }}/monitor/scripts" 19 | state: directory 20 | mode: '0755' 21 | when: skip_task != true 22 | 23 | - name: Install .env config 24 | template: 25 | src: .env.j2 26 | dest: "{{ bridge_path }}/monitor/.env" 27 | owner: "{{ compose_service_user }}" 28 | mode: '0640' 29 | when: skip_task != true 30 | 31 | - name: Copy docker-compose file 32 | copy: 33 | src: ../../../../monitor/docker-compose.yml 34 | dest: "{{ bridge_path }}/monitor/docker-compose.yml" 35 | mode: '0755' 36 | when: skip_task != true 37 | 38 | - name: Copy script file 39 | copy: 40 | src: ../../../../monitor/scripts/getBridgeStats.sh 41 | dest: "{{ bridge_path }}/monitor/scripts/getBridgeStats.sh" 42 | owner: "{{ compose_service_user }}" 43 | mode: '0755' 44 | when: skip_task != true 45 | 46 | - name: Install bridge config env 47 | template: 48 | src: config.env.j2 49 | dest: "{{ bridge_path }}/monitor/configs/{{ MONITOR_BRIDGE_NAME }}.env" 50 | owner: "{{ compose_service_user }}" 51 | mode: '0640' 52 | -------------------------------------------------------------------------------- /deployment/roles/monitor/tasks/servinstall.yml: -------------------------------------------------------------------------------- 1 | # This role creates a tokenbridge-monitor service which is designed to manage docker-compose monitor deployment. 2 | # /etc/init.d/tokenbridge-monitor start, status, stop, restart - does what the services usually do in such cases. 3 | --- 4 | - name: "Set the service" 5 | template: 6 | src: tokenbridge-monitor.j2 7 | dest: "/etc/init.d/tokenbridge-monitor" 8 | owner: root 9 | mode: 755 10 | 11 | - name: "Enable the service" 12 | service: 13 | name: "tokenbridge-monitor" 14 | state: started 15 | enabled: yes 16 | use: service 17 | 18 | - name: Start the service 19 | shell: service tokenbridge-monitor start 20 | -------------------------------------------------------------------------------- /deployment/roles/monitor/templates/.env.j2: -------------------------------------------------------------------------------- 1 | MONITOR_PORT={{ MONITOR_PORT }} 2 | -------------------------------------------------------------------------------- /deployment/roles/monitor/templates/33-monitor-docker.conf.j2: -------------------------------------------------------------------------------- 1 | $FileCreateMode 0644 2 | template(name="DockerLogFileName_Monitor" type="list") { 3 | constant(value="/var/log/docker/") 4 | property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="monitor_(.*)\\/[a-zA-Z0-9]+\\[") 5 | constant(value="/docker.log") 6 | } 7 | 8 | if $programname startswith 'monitor_' then \ 9 | ?DockerLogFileName_Monitor 10 | 11 | $FileCreateMode 0600 12 | -------------------------------------------------------------------------------- /deployment/roles/monitor/templates/38-monitor-remote-logging.conf.j2: -------------------------------------------------------------------------------- 1 | if $programname startswith 'monitor_' then { 2 | action( 3 | type="omfwd" 4 | protocol="{{ syslog_server_port.split(":")[0] }}" 5 | target="{{ (syslog_server_port.split(":")[1])[2:] }}" 6 | port="{{ syslog_server_port.split(":")[2] }}" 7 | template="RemoteForwardFormat" 8 | queue.SpoolDirectory="/var/spool/rsyslog" 9 | queue.FileName="remote" 10 | queue.MaxDiskSpace="1g" 11 | queue.SaveOnShutdown="on" 12 | queue.Type="LinkedList" 13 | ResendLastMSGOnReconnect="on" 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /deployment/roles/monitor/templates/tokenbridge-monitor.j2: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | ### BEGIN INIT INFO 4 | # Provides: tokenbridge-monitor 5 | # Required-Start: $remote_fs $syslog 6 | # Required-Stop: $remote_fs $syslog 7 | # Default-Start: 2 3 4 5 8 | # Default-Stop: 0 1 6 9 | # Short-Description: Start daemon at boot time 10 | # Description: Enable service provided by daemon. 11 | ### END INIT INFO 12 | 13 | WORKDIR="{{ '/home/' + compose_service_user | default('poadocker') + '/' + bridge_path + '/monitor' if bridge_path[:1] != "/" else bridge_path + '/monitor' }}" 14 | 15 | start(){ 16 | echo "Starting TokenBridge Monitor.." 17 | cd $WORKDIR 18 | sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v 19 | sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose rm -fv 20 | sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose up --detach 21 | sudo -u "{{ compose_service_user }}" /bin/bash -c 'cd scripts; ./getBridgeStats.sh >cronWorker.out 2>cronWorker.err' 22 | } 23 | 24 | stop(){ 25 | echo "Stopping TokenBridge Monitor.." 26 | cd $WORKDIR 27 | sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose down -v 28 | sleep 2 29 | } 30 | 31 | status(){ 32 | echo "TokenBridge Monitor status:" 33 | cd $WORKDIR 34 | sudo -u "{{ compose_service_user }}" /usr/local/bin/docker-compose ps 35 | } 36 | 37 | 38 | case "$1" in 39 | 40 | start) 41 | start 42 | ;; 43 | 44 | stop) 45 | stop 46 | ;; 47 | 48 | status) 49 | status 50 | ;; 51 | 52 | restart) 53 | echo "Restarting TokenBridge Monitor.." 54 | stop 55 | start 56 | ;; 57 | 58 | *) 59 | echo $"Usage: $0 {start|stop|restart|status}" 60 | exit 1 61 | ;; 62 | 63 | esac 64 | 65 | exit 0 66 | -------------------------------------------------------------------------------- /deployment/roles/oracle/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | bridge_path: "/home/{{ compose_service_user }}/bridge" 3 | ORACLE_ALLOW_HTTP_FOR_RPC: no 4 | ORACLE_QUEUE_URL: amqp://rabbit 5 | ORACLE_REDIS_URL: redis://redis 6 | keyfile_path: "/root/.key" 7 | -------------------------------------------------------------------------------- /deployment/roles/oracle/files/modify_to_use_syslog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | from yaml import safe_load, safe_dump 4 | from argparse import ArgumentParser 5 | from os.path import basename 6 | import sys 7 | 8 | parser = ArgumentParser() 9 | parser.add_argument('composefile', type=str, nargs=1, metavar='compose-file', help='docker-compose.yml') 10 | parser.add_argument('-d', action='store_true', help='output result instead of writing the file', dest='debug') 11 | 12 | if basename(sys.argv[0]) == "ipykernel_launcher.py": 13 | args = parser.parse_args(['docker-compose.yml']) 14 | else: 15 | args = parser.parse_args() 16 | 17 | file_to_operate = args.composefile[0] 18 | 19 | with open(file_to_operate) as composefile: 20 | composecnt=composefile.read() 21 | yml = safe_load(composecnt) 22 | for i in yml['services']: 23 | yml['services'][i]['logging'] = {'driver': 'syslog','options': {'tag': '{{.Name}}/{{.ID}}'}} 24 | if args.debug or (basename(sys.argv[0]) == "ipykernel_launcher.py"): 25 | print(safe_dump(yml)) 26 | else: 27 | with open(file_to_operate, 'w') as composefile: 28 | safe_dump(yml, composefile, explicit_start=True) -------------------------------------------------------------------------------- /deployment/roles/oracle/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: 3 | - { role: common, skip_repo: true } 4 | -------------------------------------------------------------------------------- /deployment/roles/oracle/tasks/jumpbox.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Pull the containers images 3 | shell: docker-compose pull 4 | args: 5 | chdir: "{{ bridge_path }}/oracle" 6 | when: skip_pull is undefined 7 | -------------------------------------------------------------------------------- /deployment/roles/oracle/tasks/logging.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: logging_by_syslog.yml 3 | with_items: 4 | - docker-compose 5 | - docker-compose-transfer 6 | - docker-compose-amb 7 | loop_control: 8 | loop_var: file 9 | 10 | - name: Set the oracle's containers local logs configuration file 11 | template: 12 | src: 31-oracle-docker.conf.j2 13 | dest: /etc/rsyslog.d/31-oracle-docker.conf 14 | owner: root 15 | group: root 16 | mode: 0644 17 | 18 | - name: Set the redis container local logs configuration file 19 | template: 20 | src: 32-redis-docker.conf.j2 21 | dest: /etc/rsyslog.d/32-redis-docker.conf 22 | owner: root 23 | group: root 24 | mode: 0644 25 | 26 | - name: Set the rabbit MQ container local logs configuration file 27 | template: 28 | src: 33-rabbit-docker.conf.j2 29 | dest: /etc/rsyslog.d/33-rabbit-docker.conf 30 | owner: root 31 | group: root 32 | mode: 0644 33 | 34 | - name: Set the log configuration file to send container logs to remote server 35 | template: 36 | src: 36-oracle-remote-logging.conf.j2 37 | dest: /etc/rsyslog.d/36-oracle-remote-logging.conf 38 | owner: root 39 | group: root 40 | mode: 0644 41 | when: syslog_server_port is defined 42 | 43 | - name: Discarding unwanted messages in rsyslog 44 | blockinfile: 45 | path: /etc/rsyslog.conf 46 | insertbefore: "# Where to place spool and state files" 47 | marker: "#{mark} add string to discarding unwanted messages" 48 | content: ':msg, contains, "ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY" ~' 49 | notify: restart rsyslog 50 | -------------------------------------------------------------------------------- /deployment/roles/oracle/tasks/logging_by_syslog.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Change logging facility to forward logs to syslog 3 | script: modify_to_use_syslog.py "{{ bridge_path }}/oracle/{{ file }}.yml" 4 | args: 5 | executable: python3 -------------------------------------------------------------------------------- /deployment/roles/oracle/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include_tasks: pre_config.yml 3 | - include_tasks: logging.yml 4 | - include_tasks: jumpbox.yml 5 | - include_tasks: post_config.yml 6 | - include_tasks: servinstall.yml 7 | -------------------------------------------------------------------------------- /deployment/roles/oracle/tasks/pre_config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create oracle directory 3 | file: 4 | path: "{{ bridge_path }}/oracle" 5 | state: directory 6 | mode: '0755' 7 | 8 | - name: Install .env config 9 | template: 10 | src: .env.j2 11 | dest: "{{ bridge_path }}/oracle/.env" 12 | owner: "{{ compose_service_user }}" 13 | mode: '0640' 14 | 15 | - name: Copy docker-compose files 16 | copy: 17 | src: ../../../../oracle/{{ item }} 18 | dest: "{{ bridge_path }}/oracle/" 19 | mode: '0755' 20 | with_items: 21 | - docker-compose.yml 22 | - docker-compose-transfer.yml 23 | - docker-compose-amb.yml 24 | -------------------------------------------------------------------------------- /deployment/roles/oracle/tasks/servinstall.yml: -------------------------------------------------------------------------------- 1 | # This role creates a poabridge service which is designed to manage docker-compose bridge deployment. 2 | # /etc/init.d/poabridge start, status, stop, restart - does what the services usually do in such cases. 3 | --- 4 | - name: "Set poabridge service" 5 | template: 6 | src: poabridge.j2 7 | dest: "/etc/init.d/poabridge" 8 | owner: root 9 | mode: 755 10 | 11 | - name: "Enable the service" 12 | service: 13 | name: "poabridge" 14 | state: started 15 | enabled: yes 16 | use: service 17 | 18 | - name: Start the service 19 | shell: service poabridge start 20 | -------------------------------------------------------------------------------- /deployment/roles/oracle/templates/31-oracle-docker.conf.j2: -------------------------------------------------------------------------------- 1 | $FileCreateMode 0644 2 | template(name="DockerLogFileName_Oracle" type="list") { 3 | constant(value="/var/log/docker/") 4 | property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="bridge_(.*)\\/[a-zA-Z0-9]+\\[") 5 | constant(value="/docker.log") 6 | } 7 | 8 | if $programname startswith 'oracle_bridge_' then \ 9 | ?DockerLogFileName_Oracle 10 | 11 | $FileCreateMode 0600 12 | -------------------------------------------------------------------------------- /deployment/roles/oracle/templates/32-redis-docker.conf.j2: -------------------------------------------------------------------------------- 1 | $FileCreateMode 0644 2 | template(name="DockerLogFileName_Redis" type="list") { 3 | constant(value="/var/log/docker/") 4 | property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*redis.*)\\/[a-zA-Z0-9]+\\[") 5 | constant(value="/docker.log") 6 | } 7 | 8 | if $programname contains 'oracle' and $programname contains 'redis' then \ 9 | ?DockerLogFileName_Redis 10 | 11 | $FileCreateMode 0600 12 | -------------------------------------------------------------------------------- /deployment/roles/oracle/templates/33-rabbit-docker.conf.j2: -------------------------------------------------------------------------------- 1 | $FileCreateMode 0644 2 | template(name="DockerLogFileName_Rabbit" type="list") { 3 | constant(value="/var/log/docker/") 4 | property(name="syslogtag" securepath="replace" regex.type="ERE" regex.submatch="1" regex.expression="oracle_(.*rabbit.*)\\/[a-zA-Z0-9]+\\[") 5 | constant(value="/docker.log") 6 | } 7 | 8 | if $programname contains 'oracle' and $programname contains 'rabbit' then \ 9 | ?DockerLogFileName_Rabbit 10 | 11 | $FileCreateMode 0600 12 | -------------------------------------------------------------------------------- /deployment/roles/oracle/templates/36-oracle-remote-logging.conf.j2: -------------------------------------------------------------------------------- 1 | if $programname startswith 'oracle_bridge_' then { 2 | action( 3 | type="omfwd" 4 | protocol="{{ syslog_server_port.split(":")[0] }}" 5 | target="{{ (syslog_server_port.split(":")[1])[2:] }}" 6 | port="{{ syslog_server_port.split(":")[2] }}" 7 | template="RemoteForwardFormat" 8 | queue.SpoolDirectory="/var/spool/rsyslog" 9 | queue.FileName="remote" 10 | queue.MaxDiskSpace="1g" 11 | queue.SaveOnShutdown="on" 12 | queue.Type="LinkedList" 13 | ResendLastMSGOnReconnect="on" 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /deployment/roles/oracle/templates/key.j2: -------------------------------------------------------------------------------- 1 | ## Validator-specific options 2 | ORACLE_VALIDATOR_ADDRESS={{ ORACLE_VALIDATOR_ADDRESS }} 3 | ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY={{ ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY }} 4 | -------------------------------------------------------------------------------- /deployment/site.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install Oracle 3 | hosts: oracle 4 | become: true 5 | roles: 6 | - { role: oracle } 7 | - name: Install Monitor 8 | hosts: monitor 9 | become: true 10 | roles: 11 | - { role: monitor } 12 | -------------------------------------------------------------------------------- /e2e-commons/README.md: -------------------------------------------------------------------------------- 1 | # POA TokenBridge / E2E-Commons 2 | 3 | Common scripts and configuration for the end-to-end tests. 4 | 5 | ## Usage 6 | 7 | Spin up parity networks, redis, rabbit, e2e container needed for end-to-end tests: 8 | 9 | ``` 10 | ./up.sh [components] 11 | ``` 12 | 13 | Shut down and cleans up containers, networks, services, running scripts: 14 | 15 | ``` 16 | ./down.sh 17 | ``` 18 | 19 | ### Components 20 | 21 | | Component | Description | 22 | | --- | --- | 23 | | deploy | Deploys the Smart Contracts | 24 | | oracle | Launches Oracle containers | 25 | | oracle-validator-2 | Launches Oracle containers for second validator | 26 | | oracle-validator-3 | Launches Oracle containers for third validator | 27 | | blocks | Auto mines blocks | 28 | | monitor | Launches Monitor containers | 29 | | erc-to-native | Creates infrastructure for ultimate e2e testing, for erc-to-native type of bridge | 30 | | amb | Creates infrastructure for ultimate e2e testing, for arbitrary message type of bridge | 31 | 32 | #### Ultimate e2e testing 33 | 34 | For more information on the Ultimate e2e testing, please refer to [Ultimate](./ULTIMATE.md). 35 | -------------------------------------------------------------------------------- /e2e-commons/ULTIMATE.md: -------------------------------------------------------------------------------- 1 | # POA TokenBridge / Ultimate E2E 2 | 3 | Documentation regarding the Ultimate end-to-end tests. 4 | 5 | ## Overview 6 | 7 | The ultimate e2e test scenario covers erc-to-native and amb types of bridges. 8 | It runs the e2e tests on components deployed using the deployment playbooks. 9 | 10 | 11 | ## Usage 12 | 13 | ### 1. Prepare the infrastructure 14 | 15 | Run the Parity nodes, deploy the bridge contracts, deploy Oracle using the deployment playbook. 16 | 17 | ```bash 18 | ./e2e-commons/up.sh deploy blocks 19 | ./deployment-e2e/molecule.sh ultimate-erc-to-native 20 | ``` 21 | 22 | ### 2. Run the E2E tests 23 | 24 | ```bash 25 | cd e2e-commons 26 | docker-compose run -e ULTIMATE=true e2e yarn workspace oracle-e2e run erc-to-native 27 | ``` 28 | 29 | ## Diagram 30 | 31 | ![diagram](./ultimate.png) 32 | -------------------------------------------------------------------------------- /e2e-commons/access-lists/allowance_list.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/e2e-commons/access-lists/allowance_list.txt -------------------------------------------------------------------------------- /e2e-commons/access-lists/block_list.txt: -------------------------------------------------------------------------------- 1 | 0xc9e38bfdB9c635F0796ad83CC8705dc379F41c04 2 | 0xF9698Eb93702dfdd0e2d802088d4c21822a8A977 3 | -------------------------------------------------------------------------------- /e2e-commons/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd $(dirname $0) 3 | set -e # exit when any command fails 4 | 5 | docker-compose build e2e 6 | while [ "$1" != "" ]; do 7 | if [ "$1" == "oracle" ]; then 8 | docker-compose build oracle-amb 9 | elif [ "$1" == "alm-e2e" ]; then 10 | docker-compose build oracle-amb 11 | elif [ "$1" == "monitor" ]; then 12 | docker-compose build monitor-amb 13 | elif [ "$1" == "alm" ]; then 14 | docker-compose build alm 15 | fi 16 | shift 17 | done 18 | -------------------------------------------------------------------------------- /e2e-commons/components-envs/alm.env: -------------------------------------------------------------------------------- 1 | COMMON_HOME_BRIDGE_ADDRESS=0x8397be90BCF57b0B71219f555Fe121b22e5a994C 2 | COMMON_FOREIGN_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E 3 | 4 | COMMON_HOME_RPC_URL=http://localhost:8541 5 | COMMON_FOREIGN_RPC_URL=http://localhost:8542 6 | 7 | ALM_HOME_NETWORK_NAME=Parity1 8 | ALM_FOREIGN_NETWORK_NAME=Parity2 9 | 10 | PORT=3000 11 | -------------------------------------------------------------------------------- /e2e-commons/components-envs/monitor-amb.env: -------------------------------------------------------------------------------- 1 | COMMON_HOME_RPC_URL=http://parity1:8545 2 | COMMON_FOREIGN_RPC_URL=http://parity2:8545 3 | COMMON_HOME_BRIDGE_ADDRESS=0x8397be90BCF57b0B71219f555Fe121b22e5a994C 4 | COMMON_FOREIGN_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E 5 | MONITOR_HOME_START_BLOCK=0 6 | MONITOR_FOREIGN_START_BLOCK=0 7 | MONITOR_VALIDATOR_HOME_TX_LIMIT=300000 8 | COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ 9 | COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard 10 | COMMON_HOME_GAS_PRICE_FALLBACK=1000000000 11 | COMMON_HOME_GAS_PRICE_FACTOR=1 12 | MONITOR_VALIDATOR_FOREIGN_TX_LIMIT=300000 13 | COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ 14 | COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard 15 | COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000 16 | COMMON_FOREIGN_GAS_PRICE_FACTOR=1 17 | MONITOR_TX_NUMBER_THRESHOLD=100 18 | MONITOR_PORT=3013 19 | MONITOR_BRIDGE_NAME=bridge 20 | MONITOR_CACHE_EVENTS=false 21 | -------------------------------------------------------------------------------- /e2e-commons/components-envs/monitor-erc20-native.env: -------------------------------------------------------------------------------- 1 | COMMON_HOME_RPC_URL=http://parity1:8545 2 | COMMON_FOREIGN_RPC_URL=http://parity2:8545 3 | COMMON_HOME_BRIDGE_ADDRESS=0x5118AC62AE912Dd5B51EEfF7338c4fcb0248Ba8c 4 | COMMON_FOREIGN_BRIDGE_ADDRESS=0x32198D570fffC7033641F8A9094FFDCaAEF42624 5 | MONITOR_HOME_START_BLOCK=0 6 | MONITOR_FOREIGN_START_BLOCK=0 7 | MONITOR_VALIDATOR_HOME_TX_LIMIT=300000 8 | COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ 9 | COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard 10 | COMMON_HOME_GAS_PRICE_FALLBACK=1000000000 11 | COMMON_HOME_GAS_PRICE_FACTOR=1 12 | MONITOR_VALIDATOR_FOREIGN_TX_LIMIT=300000 13 | COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ 14 | COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard 15 | COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000 16 | COMMON_FOREIGN_GAS_PRICE_FACTOR=1 17 | MONITOR_TX_NUMBER_THRESHOLD=100 18 | MONITOR_PORT=3012 19 | MONITOR_BRIDGE_NAME=bridge 20 | MONITOR_CACHE_EVENTS=false 21 | -------------------------------------------------------------------------------- /e2e-commons/components-envs/oracle-amb.env: -------------------------------------------------------------------------------- 1 | 2 | ORACLE_BRIDGE_MODE=ARBITRARY_MESSAGE 3 | ORACLE_QUEUE_URL=amqp://rabbit 4 | ORACLE_REDIS_URL=redis://redis 5 | COMMON_HOME_RPC_URL=http://parity1:8545 6 | COMMON_FOREIGN_RPC_URL=http://parity2:8545 7 | COMMON_HOME_BRIDGE_ADDRESS=0x8397be90BCF57b0B71219f555Fe121b22e5a994C 8 | COMMON_FOREIGN_BRIDGE_ADDRESS=0x1feB40aD9420b186F019A717c37f5546165d411E 9 | COMMON_HOME_GAS_PRICE_SUPPLIER_URL= 10 | COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard 11 | COMMON_HOME_GAS_PRICE_FALLBACK=1000000000 12 | ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL=600000 13 | COMMON_HOME_GAS_PRICE_FACTOR=1 14 | COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL= 15 | COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard 16 | COMMON_FOREIGN_GAS_PRICE_FALLBACK=10000000000 17 | ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000 18 | COMMON_FOREIGN_GAS_PRICE_FACTOR=1 19 | ORACLE_HOME_RPC_POLLING_INTERVAL=500 20 | ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500 21 | ORACLE_ALLOW_HTTP_FOR_RPC=yes 22 | ORACLE_HOME_START_BLOCK=1 23 | ORACLE_FOREIGN_START_BLOCK=1 24 | ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt 25 | ORACLE_FOREIGN_ARCHIVE_RPC_URL=http://parity2:8545 26 | ORACLE_HOME_EVENTS_REPROCESSING=false 27 | ORACLE_HOME_EVENTS_REPROCESSING_BATCH_SIZE=10 28 | ORACLE_HOME_EVENTS_REPROCESSING_BLOCK_DELAY=10 29 | ORACLE_FOREIGN_EVENTS_REPROCESSING=true 30 | ORACLE_FOREIGN_EVENTS_REPROCESSING_BATCH_SIZE=10 31 | ORACLE_FOREIGN_EVENTS_REPROCESSING_BLOCK_DELAY=10 32 | -------------------------------------------------------------------------------- /e2e-commons/components-envs/oracle-erc20-native.env: -------------------------------------------------------------------------------- 1 | 2 | ORACLE_BRIDGE_MODE=ERC_TO_NATIVE 3 | ORACLE_QUEUE_URL=amqp://rabbit 4 | ORACLE_REDIS_URL=redis://redis 5 | COMMON_HOME_RPC_URL=http://parity1:8545 6 | COMMON_FOREIGN_RPC_URL=http://parity2:8545 7 | COMMON_HOME_BRIDGE_ADDRESS=0x5118AC62AE912Dd5B51EEfF7338c4fcb0248Ba8c 8 | COMMON_FOREIGN_BRIDGE_ADDRESS=0x32198D570fffC7033641F8A9094FFDCaAEF42624 9 | COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ 10 | COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard 11 | COMMON_HOME_GAS_PRICE_FALLBACK=1 12 | ORACLE_HOME_GAS_PRICE_UPDATE_INTERVAL=600000 13 | COMMON_HOME_GAS_PRICE_FACTOR=1 14 | COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ 15 | COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard 16 | COMMON_FOREIGN_GAS_PRICE_FALLBACK=1 17 | ORACLE_FOREIGN_GAS_PRICE_UPDATE_INTERVAL=600000 18 | COMMON_FOREIGN_GAS_PRICE_FACTOR=0.1 19 | ORACLE_HOME_RPC_POLLING_INTERVAL=500 20 | ORACLE_FOREIGN_RPC_POLLING_INTERVAL=500 21 | ORACLE_ALLOW_HTTP_FOR_RPC=yes 22 | ORACLE_HOME_START_BLOCK=1 23 | ORACLE_FOREIGN_START_BLOCK=1 24 | ORACLE_HOME_TO_FOREIGN_BLOCK_LIST=/mono/oracle/access-lists/block_list.txt 25 | ORACLE_HOME_EVENTS_REPROCESSING=true 26 | ORACLE_HOME_EVENTS_REPROCESSING_BATCH_SIZE=10 27 | ORACLE_HOME_EVENTS_REPROCESSING_BLOCK_DELAY=10 28 | ORACLE_FOREIGN_EVENTS_REPROCESSING=true 29 | ORACLE_FOREIGN_EVENTS_REPROCESSING_BATCH_SIZE=10 30 | ORACLE_FOREIGN_EVENTS_REPROCESSING_BLOCK_DELAY=10 31 | -------------------------------------------------------------------------------- /e2e-commons/contracts-envs/amb.env: -------------------------------------------------------------------------------- 1 | BRIDGE_MODE=ARBITRARY_MESSAGE 2 | DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9 3 | HOME_DEPLOYMENT_GAS_PRICE=10000000000 4 | FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000 5 | GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50 6 | DEPLOYMENT_GAS_LIMIT_EXTRA=0.2 7 | 8 | HOME_RPC_URL=http://parity1:8545 9 | HOME_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 10 | HOME_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 11 | HOME_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 12 | HOME_MAX_AMOUNT_PER_TX=2000000 13 | HOME_REQUIRED_BLOCK_CONFIRMATIONS=1 14 | HOME_GAS_PRICE=1000000000 15 | 16 | FOREIGN_RPC_URL=http://parity2:8545 17 | FOREIGN_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 18 | FOREIGN_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 19 | FOREIGN_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 20 | FOREIGN_MAX_AMOUNT_PER_TX=2000000 21 | FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1 22 | FOREIGN_GAS_PRICE=10000000000 23 | 24 | REQUIRED_NUMBER_OF_VALIDATORS=1 25 | VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d" 26 | -------------------------------------------------------------------------------- /e2e-commons/contracts-envs/erc-to-native.env: -------------------------------------------------------------------------------- 1 | BRIDGE_MODE=ERC_TO_NATIVE 2 | DEPLOYMENT_ACCOUNT_PRIVATE_KEY=8e829f695aed89a154550f30262f1529582cc49dc30eff74a6b491359e0230f9 3 | HOME_DEPLOYMENT_GAS_PRICE=10000000000 4 | FOREIGN_DEPLOYMENT_GAS_PRICE=10000000000 5 | GET_RECEIPT_INTERVAL_IN_MILLISECONDS=50 6 | DEPLOYMENT_GAS_LIMIT_EXTRA=0.2 7 | 8 | BRIDGEABLE_TOKEN_NAME="Your New Bridged Token" 9 | BRIDGEABLE_TOKEN_SYMBOL="TEST" 10 | BRIDGEABLE_TOKEN_DECIMALS="18" 11 | 12 | HOME_RPC_URL=http://parity1:8545 13 | HOME_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 14 | HOME_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 15 | HOME_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 16 | HOME_DAILY_LIMIT=30000000000000000000000000 17 | HOME_MAX_AMOUNT_PER_TX=1500000000000000000000000 18 | HOME_MIN_AMOUNT_PER_TX=10000000000000000 19 | HOME_REQUIRED_BLOCK_CONFIRMATIONS=1 20 | HOME_GAS_PRICE=1000000000 21 | HOME_REWARDABLE=false 22 | 23 | FOREIGN_RPC_URL=http://parity2:8545 24 | FOREIGN_BRIDGE_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 25 | FOREIGN_VALIDATORS_OWNER=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 26 | FOREIGN_UPGRADEABLE_ADMIN=0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 27 | FOREIGN_DAILY_LIMIT=15000000000000000000000000 28 | FOREIGN_MAX_AMOUNT_PER_TX=750000000000000000000000 29 | FOREIGN_MIN_AMOUNT_PER_TX=10000000000000000 30 | FOREIGN_REQUIRED_BLOCK_CONFIRMATIONS=1 31 | FOREIGN_GAS_PRICE=10000000000 32 | FOREIGN_REWARDABLE=false 33 | 34 | BLOCK_REWARD_ADDRESS=0xdbeE25CbE97e4A5CC6c499875774dc7067E9426B 35 | ERC20_TOKEN_ADDRESS=0x6B175474E89094C44Da98b954EedeAC495271d0F 36 | 37 | REQUIRED_NUMBER_OF_VALIDATORS=1 38 | VALIDATORS="0xaaB52d66283F7A1D5978bcFcB55721ACB467384b 0xdCC784657C78054aa61FbcFFd2605F32374816A4 0xDcef88209a20D52165230104B245803C3269454d" 39 | -------------------------------------------------------------------------------- /e2e-commons/down.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd $(dirname $0) 3 | 4 | if [ $CI ]; then 5 | rm -rf logs || true 6 | 7 | mkdir ./logs 8 | 9 | for project in "" validator{1,2,3}; do 10 | for container in $(docker-compose -p "$project" ps | tail -n +3 | awk '{print $1}') ; do 11 | if [[ -z "$project" ]]; then 12 | path="./logs/$container.log" 13 | else 14 | mkdir -p "./logs/$project" 15 | path="./logs/$project/$container.log" 16 | fi 17 | docker logs "$container" > "$path" 2>&1 18 | done 19 | done 20 | 21 | touch ../oracle/.env 22 | for file in ../oracle/docker-compose-{amb,transfer}.yml; do 23 | for container in $(docker-compose -f "$file" ps | tail -n +3 | awk '{print $1}') ; do 24 | mkdir -p "./logs/oracle" 25 | docker logs "$container" > "./logs/oracle/$container.log" 2>&1 26 | done 27 | done 28 | 29 | exit $rc; 30 | fi 31 | 32 | ps | grep node | grep -v grep | grep -v yarn | awk '{print "kill " $1}' | /bin/bash 33 | docker-compose down 34 | docker-compose -p validator1 down 35 | docker-compose -p validator2 down 36 | docker-compose -p validator3 down 37 | docker network rm ultimate || true 38 | -------------------------------------------------------------------------------- /e2e-commons/pull.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd $(dirname $0) 3 | set -e # exit when any command fails 4 | 5 | docker-compose pull e2e 6 | while [ "$1" != "" ]; do 7 | if [ "$1" == "oracle" ]; then 8 | docker-compose pull oracle-amb 9 | elif [ "$1" == "alm-e2e" ]; then 10 | docker-compose pull oracle-amb 11 | elif [ "$1" == "monitor" ]; then 12 | docker-compose pull monitor-amb 13 | elif [ "$1" == "alm" ]; then 14 | docker-compose pull alm 15 | fi 16 | shift 17 | done 18 | -------------------------------------------------------------------------------- /e2e-commons/scripts/blocks.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3') 2 | 3 | const homeWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity1:8545')) 4 | const foreignWeb3 = new Web3(new Web3.providers.HttpProvider('http://parity2:8545')) 5 | const {user, blockGenerator} = require('../constants.json'); 6 | 7 | homeWeb3.eth.accounts.wallet.add(user.privateKey) 8 | foreignWeb3.eth.accounts.wallet.add(user.privateKey) 9 | homeWeb3.eth.accounts.wallet.add(blockGenerator.privateKey) 10 | foreignWeb3.eth.accounts.wallet.add(blockGenerator.privateKey) 11 | 12 | function generateNewBlock(web3, address) { 13 | return web3.eth.sendTransaction({ 14 | from: address, 15 | to: '0x0000000000000000000000000000000000000000', 16 | gasPrice: '1', 17 | gas: '21000', 18 | value: '1' 19 | }) 20 | } 21 | 22 | function main() { 23 | setTimeout(async () => { 24 | try { 25 | generateNewBlock(homeWeb3, blockGenerator.address) 26 | } catch (_) {} // in case of Transaction with the same hash was already imported. 27 | try { 28 | generateNewBlock(foreignWeb3, blockGenerator.address) 29 | } catch (_) {} // in case of Transaction with the same hash was already imported. 30 | main() 31 | }, 1000) 32 | } 33 | 34 | main() 35 | 36 | process.on('SIGTERM', function () { 37 | console.log('Finishing sending blocks...') 38 | process.exit(0); 39 | }); 40 | -------------------------------------------------------------------------------- /e2e-commons/scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd $(dirname $0) 3 | set -e # exit when any command fails 4 | 5 | CONTRACTS_PATH="../../contracts" 6 | DEPLOY_PATH="$CONTRACTS_PATH/deploy" 7 | ENVS_PATH="../contracts-envs" 8 | 9 | # mock bridge validators contract with the one with deterministic isValidatorDuty 10 | mv "$CONTRACTS_PATH/build/contracts/BridgeValidatorsDeterministic.json" "$CONTRACTS_PATH/build/contracts/BridgeValidators.json" 11 | 12 | echo -e "\n\n############ Deploying block reward ############\n" 13 | cp "$ENVS_PATH/erc-to-native.env" "$DEPLOY_PATH/.env" 14 | cd "$DEPLOY_PATH" 15 | node src/utils/deployBlockReward.js 16 | cd - > /dev/null 17 | 18 | echo -e "\n\n############ Deploying erc-to-native ############\n" 19 | cd "$DEPLOY_PATH" 20 | node deploy.js 21 | cd - > /dev/null 22 | 23 | echo -e "\n\n############ Deploying amb ############\n" 24 | cp "$ENVS_PATH/amb.env" "$DEPLOY_PATH/.env" 25 | cd "$DEPLOY_PATH" 26 | node deploy.js 27 | cd - > /dev/null 28 | 29 | echo -e "\n\n############ Deploying test contract for amb ############\n" 30 | cd "$DEPLOY_PATH" 31 | node src/utils/deployTestBox.js 32 | cd - > /dev/null 33 | 34 | echo -e "\n\n############ Deploying one more test contract for amb ############\n" 35 | cd "$DEPLOY_PATH" 36 | node src/utils/deployTestBox.js 37 | cd - > /dev/null 38 | 39 | echo -e "\n\n############ Deploying one more amb without oracle for confirm relay tests ############\n" 40 | cp "$ENVS_PATH/amb.env" "$DEPLOY_PATH/.env" 41 | cd "$DEPLOY_PATH" 42 | node deploy.js 43 | cd - > /dev/null 44 | -------------------------------------------------------------------------------- /e2e-commons/ultimate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/e2e-commons/ultimate.png -------------------------------------------------------------------------------- /e2e-commons/utils.js: -------------------------------------------------------------------------------- 1 | const promiseRetry = require('promise-retry') 2 | 3 | async function uniformRetry(f) { 4 | return promiseRetry(f, { 5 | forever: true, 6 | factor: 1, 7 | minTimeout: 500 8 | }) 9 | } 10 | 11 | async function sleep(timeout) { 12 | return new Promise(res => setTimeout(res, timeout)) 13 | } 14 | 15 | module.exports = { 16 | uniformRetry, 17 | sleep 18 | } 19 | -------------------------------------------------------------------------------- /monitor-e2e/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:node/recommended", 4 | "airbnb-base", 5 | "../.eslintrc" 6 | ], 7 | "plugins": ["node"], 8 | "rules": { 9 | "node/no-unpublished-require": "off", 10 | "node/no-extraneous-require": "off", 11 | "import/no-extraneous-dependencies": "off" 12 | }, 13 | "env": { 14 | "mocha": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /monitor-e2e/README.md: -------------------------------------------------------------------------------- 1 | # POA TokenBridge / Monitor-E2E 2 | 3 | End to end tests for the POA TokenBridge [Monitor](../monitor/README.md). 4 | 5 | ## Running 6 | 7 | To run the bridge end-to-end tests, you just have to run: 8 | 9 | ``` 10 | ./run-tests.sh 11 | ``` 12 | -------------------------------------------------------------------------------- /monitor-e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monitor-e2e", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "mocha --timeout 120000 --exit", 8 | "lint": "eslint . --ignore-path ../.eslintignore" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "mocha": "^5.2.0", 14 | "axios": "0.19.0" 15 | }, 16 | "engines": { 17 | "node": ">= 12.22" 18 | }, 19 | "devDependencies": {} 20 | } 21 | -------------------------------------------------------------------------------- /monitor-e2e/periodically-check-all.sh: -------------------------------------------------------------------------------- 1 | while true; do 2 | docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-erc20-native yarn check-all 3 | pid1=$! 4 | docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-amb yarn check-all 5 | pid2=$! 6 | 7 | wait $pid1 8 | wait $pid2 9 | done 10 | -------------------------------------------------------------------------------- /monitor-e2e/run-tests.sh: -------------------------------------------------------------------------------- 1 | cd $(dirname $0) 2 | 3 | mode="$1" 4 | case "$mode" in 5 | amb) 6 | script=./test/amb.js 7 | ;; 8 | erc-to-native) 9 | script=./test/ercToNative.js 10 | ;; 11 | esac 12 | 13 | MODE="$mode" ../e2e-commons/up.sh deploy blocks monitor 14 | 15 | MODE="$mode" ./wait-for-monitor.sh 16 | nohup ./periodically-check-all.sh < /dev/null > /dev/null 2>&1 & 17 | checkPID=$! 18 | 19 | docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace monitor-e2e run start $script 20 | rc=$? 21 | 22 | ../e2e-commons/down.sh 23 | kill $checkPID 24 | exit $rc 25 | -------------------------------------------------------------------------------- /monitor-e2e/test/ercToNative.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | const axios = require('axios') 3 | const { ercToNativeBridge, user, foreignRPC, validator } = require('../../e2e-commons/constants.json') 4 | const { waitUntil, sendTokens, addValidator } = require('../utils') 5 | 6 | const baseUrl = ercToNativeBridge.monitor 7 | 8 | describe('ERC TO NATIVE', () => { 9 | let data 10 | 11 | before(async () => { 12 | ;({ data } = await axios.get(`${baseUrl}`)) 13 | }) 14 | 15 | it('balance', () => assert(parseInt(data.foreign.erc20Balance, 10) >= 0)) 16 | it('should contain totalSupply', () => assert(data.home.totalSupply === '0')) 17 | }) 18 | 19 | describe('ERC TO NATIVE with changing state of contracts', () => { 20 | let data 21 | 22 | before(async () => { 23 | assert((await axios.get(`${baseUrl}`)).data.balanceDiff === 0) 24 | assert((await axios.get(`${baseUrl}/validators`)).data.validatorsMatch === true) 25 | }) 26 | 27 | it('should change balanceDiff', async function() { 28 | this.timeout(60000) 29 | await sendTokens(foreignRPC.URL, user, ercToNativeBridge.foreignToken, ercToNativeBridge.foreign) 30 | 31 | await waitUntil(async () => { 32 | ;({ data } = await axios.get(`${baseUrl}`)) 33 | if (!data.foreign) { 34 | return false 35 | } 36 | const { erc20Balance, investedErc20Balance } = data.foreign 37 | return data.balanceDiff === 0.01 && erc20Balance === '0.01' && investedErc20Balance === undefined 38 | }) 39 | }) 40 | 41 | it('should change validatorsMatch', async () => { 42 | await addValidator(foreignRPC.URL, validator, ercToNativeBridge.foreign) 43 | await waitUntil(async () => { 44 | ;({ data } = await axios.get(`${baseUrl}/validators`)) 45 | return data.validatorsMatch === false 46 | }) 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /monitor-e2e/wait-for-monitor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | FILES=(getBalances.json validators.json eventsStats.json alerts.json) 4 | 5 | check_files_exist() { 6 | rc=0 7 | for f in "${FILES[@]}"; do 8 | command="test -f responses/bridge/$f" 9 | if [[ -z "$MODE" || "$MODE" == erc-to-native ]]; then 10 | (docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-erc20-native /bin/bash -c "$command") || rc=1 11 | fi 12 | if [[ -z "$MODE" || "$MODE" == amb ]]; then 13 | (docker-compose -f ../e2e-commons/docker-compose.yml exec -T monitor-amb /bin/bash -c "$command") || rc=1 14 | fi 15 | done 16 | return $rc 17 | } 18 | 19 | i=0 20 | until check_files_exist 21 | do 22 | ((i++)) 23 | if [ "$i" -gt 30 ] 24 | then 25 | exit -1 26 | fi 27 | 28 | echo "Waiting for monitor to start..." 29 | sleep 3 30 | done 31 | 32 | echo "Monitor started" 33 | -------------------------------------------------------------------------------- /monitor/.env.example: -------------------------------------------------------------------------------- 1 | MONITOR_BRIDGE_NAME=bridge 2 | 3 | COMMON_HOME_RPC_URL=https://sokol.poa.network 4 | COMMON_FOREIGN_RPC_URL=https://kovan.infura.io/mew 5 | COMMON_HOME_BRIDGE_ADDRESS=0xABb4C1399DcC28FBa3Beb76CAE2b50Be3e087353 6 | COMMON_FOREIGN_BRIDGE_ADDRESS=0xE405F6872cE38a7a4Ff63DcF946236D458c2ca3a 7 | MONITOR_HOME_START_BLOCK=0 8 | MONITOR_FOREIGN_START_BLOCK=0 9 | 10 | MONITOR_VALIDATOR_HOME_TX_LIMIT=300000 11 | COMMON_HOME_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ 12 | COMMON_HOME_GAS_PRICE_SPEED_TYPE=standard 13 | COMMON_HOME_GAS_PRICE_FALLBACK=1000000000 14 | COMMON_HOME_GAS_PRICE_FACTOR=1 15 | 16 | MONITOR_VALIDATOR_FOREIGN_TX_LIMIT=300000 17 | COMMON_FOREIGN_GAS_PRICE_SUPPLIER_URL=https://gasprice.poa.network/ 18 | COMMON_FOREIGN_GAS_PRICE_SPEED_TYPE=standard 19 | COMMON_FOREIGN_GAS_PRICE_FALLBACK=1000000000 20 | COMMON_FOREIGN_GAS_PRICE_FACTOR=1 21 | 22 | MONITOR_TX_NUMBER_THRESHOLD=100 23 | MONITOR_PORT=3003 24 | MONITOR_CACHE_EVENTS=true 25 | 26 | MONITOR_HOME_TO_FOREIGN_ALLOWANCE_LIST= 27 | MONITOR_HOME_TO_FOREIGN_BLOCK_LIST= 28 | 29 | # MONITOR_HOME_VALIDATORS_BALANCE_ENABLE=0x... 0x... 0x... 30 | # MONITOR_FOREIGN_VALIDATORS_BALANCE_ENABLE=0x... 0x... 0x... 31 | -------------------------------------------------------------------------------- /monitor/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["node"], 3 | "extends": [ 4 | "plugin:node/recommended", 5 | "airbnb-base", 6 | "../.eslintrc" 7 | ], 8 | "rules": { 9 | "no-use-before-define": "off", 10 | "node/no-unpublished-require": "off" 11 | }, 12 | "env": { 13 | "mocha": true 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /monitor/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 as contracts 2 | 3 | WORKDIR /mono 4 | 5 | COPY contracts/package.json contracts/package-lock.json ./contracts/ 6 | 7 | WORKDIR /mono/contracts 8 | RUN npm install --only=prod 9 | 10 | COPY ./contracts/truffle-config.js ./ 11 | COPY ./contracts/contracts ./contracts 12 | RUN npm run compile 13 | 14 | FROM node:12 15 | 16 | WORKDIR /mono 17 | COPY package.json . 18 | COPY --from=contracts /mono/contracts/build ./contracts/build 19 | COPY commons/package.json ./commons/ 20 | COPY monitor/package.json ./monitor/ 21 | COPY yarn.lock . 22 | RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production 23 | 24 | COPY ./commons ./commons 25 | COPY ./monitor ./monitor 26 | 27 | WORKDIR /mono/monitor 28 | CMD echo "To start the monitor web service run:" \ 29 | "yarn start" \ 30 | "To run monitor scripts run:" \ 31 | "yarn check-all" 32 | -------------------------------------------------------------------------------- /monitor/cache/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/monitor/cache/.gitkeep -------------------------------------------------------------------------------- /monitor/checkWorker3.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const logger = require('./logger')('checkWorker3') 3 | const detectMediators = require('./detectMediators') 4 | const detectFailures = require('./detectFailures') 5 | const { writeFile, createDir } = require('./utils/file') 6 | const { web3Home } = require('./utils/web3') 7 | const { saveCache } = require('./utils/web3Cache') 8 | 9 | const { MONITOR_BRIDGE_NAME, COMMON_HOME_BRIDGE_ADDRESS } = process.env 10 | const { getBridgeMode, HOME_ERC_TO_NATIVE_ABI, BRIDGE_MODES } = require('../commons') 11 | 12 | async function checkWorker3() { 13 | try { 14 | const homeBridge = new web3Home.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS) 15 | const bridgeMode = await getBridgeMode(homeBridge) 16 | if (bridgeMode === BRIDGE_MODES.ARBITRARY_MESSAGE) { 17 | createDir(`/responses/${MONITOR_BRIDGE_NAME}`) 18 | 19 | logger.debug('calling detectMediators()') 20 | const mediators = await detectMediators(bridgeMode) 21 | mediators.ok = true 22 | mediators.health = true 23 | writeFile(`/responses/${MONITOR_BRIDGE_NAME}/mediators.json`, mediators) 24 | 25 | logger.debug('calling detectFailures()') 26 | const failures = await detectFailures(bridgeMode) 27 | failures.ok = true 28 | failures.health = true 29 | writeFile(`/responses/${MONITOR_BRIDGE_NAME}/failures.json`, failures) 30 | 31 | saveCache() 32 | logger.debug('Done') 33 | } 34 | } catch (e) { 35 | logger.error('checkWorker3.js', e) 36 | } 37 | } 38 | checkWorker3() 39 | -------------------------------------------------------------------------------- /monitor/configs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/monitor/configs/.gitkeep -------------------------------------------------------------------------------- /monitor/crontab.example: -------------------------------------------------------------------------------- 1 | # Yarn: 2 | */4 * * * * cd $HOME/tokenbridge/monitor; yarn check-all >>cronWorker.out 2>>cronWorker.err 3 | 4 | # Docker: 5 | */4 * * * * cd $HOME/tokenbridge/monitor; docker run --rm --env-file .env -v $(pwd)/responses:/mono/monitor/responses poanetwork/tokenbridge-monitor:latest /bin/bash -c 'yarn check-all' >>cronWorker.out 2>>cronWorker.err 6 | -------------------------------------------------------------------------------- /monitor/docker-compose-build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '2.4' 3 | services: 4 | monitor: 5 | image: poanetwork/tokenbridge-monitor 6 | build: 7 | context: .. 8 | dockerfile: ./monitor/Dockerfile 9 | -------------------------------------------------------------------------------- /monitor/docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '2.4' 3 | services: 4 | monitor: 5 | image: poanetwork/tokenbridge-monitor:latest 6 | ports: 7 | - "${MONITOR_PORT}:${MONITOR_PORT}" 8 | env_file: ./.env 9 | environment: 10 | - NODE_ENV=production 11 | volumes: 12 | - ./responses:/mono/monitor/responses 13 | - ./cache:/mono/monitor/cache 14 | restart: unless-stopped 15 | entrypoint: "yarn start" 16 | -------------------------------------------------------------------------------- /monitor/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const express = require('express') 3 | const cors = require('cors') 4 | const { readFile } = require('./utils/file') 5 | 6 | const app = express() 7 | const bridgeRouter = express.Router({ mergeParams: true }) 8 | 9 | app.use(cors()) 10 | 11 | app.get('/favicon.ico', (req, res) => res.sendStatus(204)) 12 | app.use('/:bridgeName', bridgeRouter) 13 | 14 | bridgeRouter.get('/:file(validators|eventsStats|alerts|mediators|stuckTransfers|failures)?', (req, res, next) => { 15 | try { 16 | const { bridgeName, file } = req.params 17 | const results = readFile(`./responses/${bridgeName}/${file || 'getBalances'}.json`) 18 | res.json(results) 19 | } catch (e) { 20 | // this will eventually be handled by your error handling middleware 21 | next(e) 22 | } 23 | }) 24 | 25 | bridgeRouter.get('/metrics', (req, res, next) => { 26 | try { 27 | const { bridgeName } = req.params 28 | const metrics = readFile(`./responses/${bridgeName}/metrics.txt`, false) 29 | res.type('text').send(metrics) 30 | } catch (e) { 31 | next(e) 32 | } 33 | }) 34 | 35 | const port = process.env.MONITOR_PORT || 3003 36 | app.set('port', port) 37 | app.listen(port, () => console.log(`Monitoring app listening on port ${port}!`)) 38 | -------------------------------------------------------------------------------- /monitor/logger.js: -------------------------------------------------------------------------------- 1 | module.exports = function logger(name) { 2 | let lastlog = 0 3 | 4 | function log(...args) { 5 | const now = new Date() 6 | console.log(now.toISOString(), `(+${lastlog ? now.getTime() - lastlog : 0}ms)`, `[${name}]`, ...args) 7 | lastlog = now.getTime() 8 | } 9 | 10 | function error(...args) { 11 | const now = new Date() 12 | console.error(now.toISOString(), `[${name}]`, ...args) 13 | } 14 | 15 | let dbg 16 | if (process.env.DEBUG) { 17 | dbg = (...args) => { 18 | log(...args) 19 | } 20 | } else { 21 | dbg = () => {} 22 | } 23 | 24 | return { 25 | log, 26 | error, 27 | debug: dbg 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /monitor/metricsWorker.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const logger = require('./logger')('metricsWorker') 3 | const { writeFile, createDir } = require('./utils/file') 4 | const getPrometheusMetrics = require('./prometheusMetrics') 5 | 6 | const { MONITOR_BRIDGE_NAME } = process.env 7 | 8 | async function metricsWorker() { 9 | try { 10 | createDir(`/responses/${MONITOR_BRIDGE_NAME}`) 11 | logger.debug('calling getPrometheusMetrics()') 12 | const metrics = await getPrometheusMetrics(MONITOR_BRIDGE_NAME) 13 | if (!metrics) throw new Error('metrics is empty: ' + JSON.stringify(metrics)) 14 | writeFile(`/responses/${MONITOR_BRIDGE_NAME}/metrics.txt`, metrics, { stringify: false }) 15 | logger.debug('Done') 16 | } catch (e) { 17 | logger.error(e) 18 | } 19 | } 20 | metricsWorker() 21 | -------------------------------------------------------------------------------- /monitor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "monitor", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "check-all": "timeout -s 9 5m node checkWorker.js && timeout -s 9 5m node checkWorker2.js && timeout -s 9 5m node checkWorker3.js && timeout -s 9 10s node metricsWorker.js", 8 | "start": "node index.js", 9 | "check-and-start": "yarn check-all && yarn start", 10 | "lint": "eslint . --ignore-path ../.eslintignore", 11 | "lint:fix": "eslint . --fix", 12 | "test": "NODE_ENV=test mocha" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "bignumber.js": "^9.0.1", 18 | "cors": "^2.8.5", 19 | "dotenv": "^5.0.1", 20 | "express": "^4.16.3", 21 | "node-fetch": "^2.1.2", 22 | "web3": "^1.3.0" 23 | }, 24 | "engines": { 25 | "node": ">= 12.22" 26 | }, 27 | "devDependencies": { 28 | "chai": "^4.2.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /monitor/responses/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/monitor/responses/.gitkeep -------------------------------------------------------------------------------- /monitor/test-srv.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | 3 | const app = express() 4 | 5 | app.all('/', (req, res) => { 6 | setTimeout(() => { 7 | res.status(504) 8 | res.end() 9 | }, 2000) 10 | }) 11 | 12 | const MONITOR_PORT = process.env.MONITOR_PORT || 4000 13 | app.listen(MONITOR_PORT, () => console.log('Listening on port ' + MONITOR_PORT)) 14 | -------------------------------------------------------------------------------- /monitor/utils/web3.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config() 2 | const Web3 = require('web3') 3 | 4 | const { COMMON_HOME_RPC_URL, COMMON_FOREIGN_RPC_URL } = process.env 5 | 6 | const homeProvider = new Web3.providers.HttpProvider(COMMON_HOME_RPC_URL) 7 | const web3Home = new Web3(homeProvider) 8 | 9 | const foreignProvider = new Web3.providers.HttpProvider(COMMON_FOREIGN_RPC_URL) 10 | const web3Foreign = new Web3(foreignProvider) 11 | 12 | function blockNumberWrapper(web3) { 13 | let blockNumber = null 14 | return async () => { 15 | if (!blockNumber) { 16 | blockNumber = await web3.eth.getBlockNumber() 17 | } 18 | return blockNumber 19 | } 20 | } 21 | 22 | module.exports = { 23 | web3Home, 24 | web3Foreign, 25 | getHomeBlockNumber: blockNumberWrapper(web3Home), 26 | getForeignBlockNumber: blockNumberWrapper(web3Foreign) 27 | } 28 | -------------------------------------------------------------------------------- /oracle-e2e/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:node/recommended", 4 | "airbnb-base", 5 | "../.eslintrc" 6 | ], 7 | "plugins": ["node"], 8 | "rules": { 9 | "node/no-unpublished-require": "off" 10 | }, 11 | "env": { 12 | "mocha": true 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /oracle-e2e/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oracle-e2e", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "mocha", 8 | "generate-amb-tx": "node ./scripts/generate-amb-tx.js", 9 | "lint": "eslint . --ignore-path ../.eslintignore", 10 | "amb": "mocha test/amb.js", 11 | "erc-to-native": "mocha test/ercToNative.js", 12 | "alm": "mocha test/amb.js" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "mocha": "^5.2.0", 18 | "chalk": "^2.4.1", 19 | "dotenv": "^6.0.0", 20 | "promise-retry": "^1.1.1", 21 | "shelljs": "^0.8.2", 22 | "tree-kill": "^1.2.0", 23 | "web3": "^1.3.0", 24 | "websocket": "^1.0.28" 25 | }, 26 | "engines": { 27 | "node": ">= 12.22" 28 | }, 29 | "devDependencies": {} 30 | } 31 | -------------------------------------------------------------------------------- /oracle-e2e/run-tests.sh: -------------------------------------------------------------------------------- 1 | cd $(dirname $0) 2 | 3 | mode="$1" 4 | case "$mode" in 5 | amb) 6 | script=./test/amb.js 7 | ;; 8 | erc-to-native) 9 | script=./test/ercToNative.js 10 | ;; 11 | esac 12 | 13 | MODE="$mode" ../e2e-commons/up.sh deploy generate-amb-tx manual-amb-relay blocks oracle oracle-validator-2 oracle-validator-3 14 | 15 | docker-compose -f ../e2e-commons/docker-compose.yml run e2e yarn workspace oracle-e2e run start $script 16 | rc=$? 17 | 18 | ../e2e-commons/down.sh 19 | exit $rc 20 | -------------------------------------------------------------------------------- /oracle-e2e/scripts/generate-amb-tx.js: -------------------------------------------------------------------------------- 1 | const Web3 = require('web3') 2 | const { user, homeRPC, foreignRPC, amb2: amb } = require('../../e2e-commons/constants.json') 3 | const { BOX_ABI } = require('../../commons') 4 | 5 | const homeWeb3 = new Web3(new Web3.providers.HttpProvider(homeRPC.URL)) 6 | const foreignWeb3 = new Web3(new Web3.providers.HttpProvider(foreignRPC.URL)) 7 | 8 | homeWeb3.eth.accounts.wallet.add(user.privateKey) 9 | foreignWeb3.eth.accounts.wallet.add(user.privateKey) 10 | 11 | const opts = { 12 | from: user.address, 13 | gas: 400000, 14 | gasPrice: '1' 15 | } 16 | const homeBox = new homeWeb3.eth.Contract(BOX_ABI, amb.homeBox, opts) 17 | const foreignBox = new foreignWeb3.eth.Contract(BOX_ABI, amb.foreignBox, opts) 18 | 19 | async function main() { 20 | const res1 = await homeBox.methods.setValueOnOtherNetwork(123, amb.home, amb.foreignBox).send() 21 | const res2 = await foreignBox.methods.setValueOnOtherNetwork(456, amb.foreign, amb.homeBox).send() 22 | const res3 = await foreignBox.methods.setValueOnOtherNetwork(789, amb.foreign, amb.homeBox).send() 23 | 24 | console.log(res1.transactionHash) 25 | console.log(res2.transactionHash) 26 | console.log(res3.transactionHash) 27 | } 28 | 29 | main() 30 | -------------------------------------------------------------------------------- /oracle-e2e/test/mocha.opts: -------------------------------------------------------------------------------- 1 | --timeout 120000 --exit 2 | -------------------------------------------------------------------------------- /oracle-e2e/test/utils.js: -------------------------------------------------------------------------------- 1 | const { BRIDGE_VALIDATORS_ABI } = require('../../commons') 2 | 3 | async function delay(ms) { 4 | return new Promise(res => setTimeout(res, ms)) 5 | } 6 | 7 | const setRequiredSignatures = async ({ bridgeContract, web3, requiredSignatures, options }) => { 8 | const validatorAddress = await bridgeContract.methods.validatorContract().call() 9 | const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorAddress) 10 | 11 | return validatorContract.methods.setRequiredSignatures(requiredSignatures).send(options) 12 | } 13 | 14 | module.exports = { 15 | delay, 16 | setRequiredSignatures 17 | } 18 | -------------------------------------------------------------------------------- /oracle/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:node/recommended", 4 | "airbnb-base", 5 | "../.eslintrc" 6 | ], 7 | "plugins": ["node"], 8 | "rules": { 9 | "node/no-unpublished-require": "off", 10 | "global-require": "off" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /oracle/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12 as contracts 2 | 3 | WORKDIR /mono 4 | 5 | COPY contracts/package.json contracts/package-lock.json ./contracts/ 6 | 7 | WORKDIR /mono/contracts 8 | RUN npm install --only=prod 9 | 10 | COPY ./contracts/truffle-config.js ./ 11 | COPY ./contracts/contracts ./contracts 12 | RUN npm run compile 13 | 14 | FROM node:12 15 | 16 | RUN apt-get update && \ 17 | apt-get install -y build-essential libc6-dev libc6-dev-i386 wget && \ 18 | apt-get clean 19 | 20 | WORKDIR /mono 21 | COPY package.json . 22 | COPY --from=contracts /mono/contracts/build ./contracts/build 23 | COPY commons/package.json ./commons/ 24 | COPY oracle/package.json ./oracle/ 25 | COPY yarn.lock . 26 | RUN NOYARNPOSTINSTALL=1 yarn install --frozen-lockfile --production 27 | 28 | COPY ./commons ./commons 29 | COPY ./oracle ./oracle 30 | 31 | WORKDIR /mono/oracle 32 | CMD echo "To start a bridge process run:" \ 33 | "ORACLE_VALIDATOR_ADDRESS= ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY= docker-compose up -d --build" 34 | -------------------------------------------------------------------------------- /oracle/ERC_TO_NATIVE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omni/tokenbridge/961b12b9f3545830a04044e109762277efcea6ef/oracle/ERC_TO_NATIVE.png -------------------------------------------------------------------------------- /oracle/config/affirmation-request-watcher.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | 3 | const id = `${baseConfig.id}-affirmation-request` 4 | 5 | module.exports = { 6 | ...baseConfig, 7 | main: baseConfig.foreign, 8 | event: 'UserRequestForAffirmation', 9 | sender: 'home', 10 | queue: 'home-prioritized', 11 | name: `watcher-${id}`, 12 | id 13 | } 14 | -------------------------------------------------------------------------------- /oracle/config/collected-signatures-watcher.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | 3 | const id = `${baseConfig.id}-collected-signatures` 4 | 5 | module.exports = { 6 | ...baseConfig, 7 | main: baseConfig.home, 8 | event: 'CollectedSignatures', 9 | sender: 'foreign', 10 | queue: 'foreign-prioritized', 11 | name: `watcher-${id}`, 12 | id 13 | } 14 | -------------------------------------------------------------------------------- /oracle/config/foreign-mev-sender.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | 3 | const { DEFAULT_TRANSACTION_RESEND_INTERVAL } = require('../src/utils/constants') 4 | const { MEV_HELPER_ABI } = require('../src/utils/mev') 5 | const { web3Foreign, getFlashbotsProvider } = require('../src/services/web3') 6 | 7 | const { 8 | ORACLE_FOREIGN_TX_RESEND_INTERVAL, 9 | ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS, 10 | ORACLE_MEV_FOREIGN_MIN_GAS_PRICE, 11 | ORACLE_MEV_FOREIGN_FLAT_MINER_FEE, 12 | ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS, 13 | ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS, 14 | ORACLE_MEV_FOREIGN_BUNDLES_BLOCK_RANGE 15 | } = process.env 16 | 17 | const contract = new baseConfig.foreign.web3.eth.Contract(MEV_HELPER_ABI, ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS) 18 | 19 | module.exports = { 20 | ...baseConfig, 21 | pollingInterval: baseConfig.foreign.pollingInterval, 22 | mevForeign: { 23 | contractAddress: ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS, 24 | contract, 25 | minGasPrice: ORACLE_MEV_FOREIGN_MIN_GAS_PRICE, 26 | flatMinerFee: ORACLE_MEV_FOREIGN_FLAT_MINER_FEE, 27 | maxPriorityFeePerGas: ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS, 28 | maxFeePerGas: ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS, 29 | bundlesPerIteration: Math.max(parseInt(ORACLE_MEV_FOREIGN_BUNDLES_BLOCK_RANGE, 10) || 5, 1), 30 | getFlashbotsProvider 31 | }, 32 | mevJobsRedisKey: `${baseConfig.id}-collected-signatures-mev:mevJobs`, 33 | id: 'mev-sender-foreign', 34 | name: 'mev-sender-foreign', 35 | web3: web3Foreign, 36 | resendInterval: parseInt(ORACLE_FOREIGN_TX_RESEND_INTERVAL, 10) || DEFAULT_TRANSACTION_RESEND_INTERVAL 37 | } 38 | -------------------------------------------------------------------------------- /oracle/config/foreign-sender.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | 3 | const { DEFAULT_TRANSACTION_RESEND_INTERVAL } = require('../src/utils/constants') 4 | 5 | const { ORACLE_FOREIGN_TX_RESEND_INTERVAL } = process.env 6 | 7 | module.exports = { 8 | ...baseConfig, 9 | main: baseConfig.foreign, 10 | queue: 'foreign-prioritized', 11 | id: 'foreign', 12 | name: 'sender-foreign', 13 | resendInterval: parseInt(ORACLE_FOREIGN_TX_RESEND_INTERVAL, 10) || DEFAULT_TRANSACTION_RESEND_INTERVAL 14 | } 15 | -------------------------------------------------------------------------------- /oracle/config/home-sender.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | 3 | const { DEFAULT_TRANSACTION_RESEND_INTERVAL } = require('../src/utils/constants') 4 | 5 | const { ORACLE_HOME_TX_RESEND_INTERVAL } = process.env 6 | 7 | module.exports = { 8 | ...baseConfig, 9 | main: baseConfig.home, 10 | queue: 'home-prioritized', 11 | id: 'home', 12 | name: 'sender-home', 13 | resendInterval: parseInt(ORACLE_HOME_TX_RESEND_INTERVAL, 10) || DEFAULT_TRANSACTION_RESEND_INTERVAL 14 | } 15 | -------------------------------------------------------------------------------- /oracle/config/information-request-watcher.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | 3 | const id = `${baseConfig.id}-information-request` 4 | 5 | module.exports = { 6 | ...baseConfig, 7 | main: baseConfig.home, 8 | event: 'UserRequestForInformation', 9 | sender: 'home', 10 | queue: 'home-prioritized', 11 | name: `watcher-${id}`, 12 | id 13 | } 14 | -------------------------------------------------------------------------------- /oracle/config/mev-collected-signatures-watcher.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | const { MEV_HELPER_ABI } = require('../src/utils/mev') 3 | 4 | const { 5 | ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS, 6 | ORACLE_MEV_FOREIGN_MIN_GAS_PRICE, 7 | ORACLE_MEV_FOREIGN_FLAT_MINER_FEE, 8 | ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS, 9 | ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS 10 | } = process.env 11 | 12 | const id = `${baseConfig.id}-collected-signatures-mev` 13 | 14 | const contract = new baseConfig.foreign.web3.eth.Contract(MEV_HELPER_ABI, ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS) 15 | 16 | module.exports = { 17 | ...baseConfig, 18 | mevForeign: { 19 | contractAddress: ORACLE_MEV_FOREIGN_HELPER_CONTRACT_ADDRESS, 20 | contract, 21 | minGasPrice: ORACLE_MEV_FOREIGN_MIN_GAS_PRICE, 22 | flatMinerFee: ORACLE_MEV_FOREIGN_FLAT_MINER_FEE, 23 | maxPriorityFeePerGas: ORACLE_MEV_FOREIGN_MAX_PRIORITY_FEE_PER_GAS, 24 | maxFeePerGas: ORACLE_MEV_FOREIGN_MAX_FEE_PER_GAS 25 | }, 26 | main: baseConfig.home, 27 | event: 'CollectedSignatures', 28 | name: `watcher-${id}`, 29 | id 30 | } 31 | -------------------------------------------------------------------------------- /oracle/config/shutdown-manager.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | 3 | const { 4 | ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL, 5 | ORACLE_SHUTDOWN_SERVICE_URL, 6 | ORACLE_SHUTDOWN_CONTRACT_ADDRESS, 7 | ORACLE_SHUTDOWN_CONTRACT_METHOD 8 | } = process.env 9 | 10 | module.exports = { 11 | ...baseConfig, 12 | id: 'shutdown-manager', 13 | name: 'shutdown-manager', 14 | pollingInterval: ORACLE_SHUTDOWN_SERVICE_POLLING_INTERVAL || 120000, 15 | checksBeforeResume: 3, 16 | checksBeforeStop: 1, 17 | shutdownServiceURL: ORACLE_SHUTDOWN_SERVICE_URL, 18 | shutdownContractAddress: ORACLE_SHUTDOWN_CONTRACT_ADDRESS, 19 | shutdownMethod: (ORACLE_SHUTDOWN_CONTRACT_METHOD || 'isShutdown()').trim(), 20 | requestTimeout: 2000 21 | } 22 | -------------------------------------------------------------------------------- /oracle/config/signature-request-watcher.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | 3 | const id = `${baseConfig.id}-signature-request` 4 | 5 | module.exports = { 6 | ...baseConfig, 7 | main: baseConfig.home, 8 | event: 'UserRequestForSignature', 9 | sender: 'home', 10 | queue: 'home-prioritized', 11 | name: `watcher-${id}`, 12 | id 13 | } 14 | -------------------------------------------------------------------------------- /oracle/config/transfer-watcher.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('./base.config') 2 | const { ERC20_ABI, ZERO_ADDRESS } = require('../../commons') 3 | const { EXIT_CODES } = require('../src/utils/constants') 4 | 5 | const id = `${baseConfig.id}-transfer` 6 | 7 | if (baseConfig.id !== 'erc-native') { 8 | console.error(`Transfer watcher not required for bridge mode ${process.env.ORACLE_BRIDGE_MODE}`) 9 | process.exit(EXIT_CODES.WATCHER_NOT_REQUIRED) 10 | } 11 | 12 | // exact address of the token contract is set in the watcher.js checkConditions() function 13 | baseConfig.foreign.eventContract = new baseConfig.foreign.web3.eth.Contract(ERC20_ABI, ZERO_ADDRESS) 14 | 15 | module.exports = { 16 | ...baseConfig, 17 | main: baseConfig.foreign, 18 | event: 'Transfer', 19 | eventFilter: { to: baseConfig.foreign.bridgeAddress }, 20 | sender: 'home', 21 | queue: 'home-prioritized', 22 | name: `watcher-${id}`, 23 | id 24 | } 25 | -------------------------------------------------------------------------------- /oracle/docker-compose-build.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: '2.4' 3 | services: 4 | oracle: 5 | image: poanetwork/tokenbridge-oracle 6 | build: 7 | context: .. 8 | dockerfile: ./oracle/Dockerfile 9 | -------------------------------------------------------------------------------- /oracle/env.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | require('dotenv').config({ 3 | path: path.join(__dirname, process.env.NODE_ENV === 'test' ? './test/test.env' : './.env') 4 | }) 5 | -------------------------------------------------------------------------------- /oracle/esController.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: GPL-3.0-only 2 | 3 | /* 4 | This contract can be used together with the emergency shutdown 5 | functionality of the TokenBridge oracles. 6 | */ 7 | 8 | pragma solidity 0.7.6; 9 | 10 | contract PauseController { 11 | address public manager; 12 | bool internal paused; 13 | bytes32 private immutable ID; 14 | 15 | constructor (string memory _id, address _manager) { 16 | require(bytes(_id).length <= 32); 17 | bytes32 id; 18 | assembly { 19 | id := mload(add(_id, 32)) 20 | } 21 | ID = id; 22 | 23 | manager = _manager; 24 | } 25 | 26 | modifier onlyManager() { 27 | require(msg.sender == manager); 28 | _; 29 | } 30 | 31 | function changeManager(address _newmanager) external onlyManager { 32 | require(_newmanager != address(0)); 33 | manager = _newmanager; 34 | } 35 | 36 | function pause() external onlyManager { 37 | paused = true; 38 | } 39 | 40 | function play() external onlyManager { 41 | paused = false; 42 | } 43 | 44 | function isPaused() external view returns(bool) { 45 | return paused; 46 | } 47 | 48 | function id() external view returns(string memory) { 49 | return string(abi.encodePacked(ID)); 50 | } 51 | 52 | } -------------------------------------------------------------------------------- /oracle/reset-lastBlock.sh: -------------------------------------------------------------------------------- 1 | node scripts/resetLastBlock.js $1 $2 2 | -------------------------------------------------------------------------------- /oracle/scripts/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "rules": { 4 | "import/no-extraneous-dependencies": "off", 5 | "node/no-unpublished-require": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /oracle/scripts/erc20_to_native/sendHome.js: -------------------------------------------------------------------------------- 1 | require('../../env') 2 | const { web3Home } = require('../../src/services/web3') 3 | const { sendTx } = require('../../src/tx/sendTx') 4 | const { isValidAmount } = require('../utils/utils') 5 | const { HOME_ERC_TO_NATIVE_ABI } = require('../../../commons') 6 | 7 | const { 8 | USER_ADDRESS, 9 | USER_ADDRESS_PRIVATE_KEY, 10 | COMMON_HOME_BRIDGE_ADDRESS, 11 | HOME_MIN_AMOUNT_PER_TX, 12 | HOME_TEST_TX_GAS_PRICE 13 | } = process.env 14 | 15 | const NUMBER_OF_DEPOSITS_TO_SEND = process.argv[2] || 1 16 | 17 | async function main() { 18 | const bridge = new web3Home.eth.Contract(HOME_ERC_TO_NATIVE_ABI, COMMON_HOME_BRIDGE_ADDRESS) 19 | 20 | try { 21 | await isValidAmount(HOME_MIN_AMOUNT_PER_TX, bridge) 22 | 23 | const homeChainId = await web3Home.eth.getChainId() 24 | let nonce = await web3Home.eth.getTransactionCount(USER_ADDRESS) 25 | let actualSent = 0 26 | for (let i = 0; i < Number(NUMBER_OF_DEPOSITS_TO_SEND); i++) { 27 | const txHash = await sendTx({ 28 | privateKey: USER_ADDRESS_PRIVATE_KEY, 29 | data: '0x', 30 | nonce, 31 | gasPrice: HOME_TEST_TX_GAS_PRICE, 32 | value: web3Home.utils.toWei(HOME_MIN_AMOUNT_PER_TX), 33 | gasLimit: 100000, 34 | to: COMMON_HOME_BRIDGE_ADDRESS, 35 | web3: web3Home, 36 | chainId: homeChainId 37 | }) 38 | if (txHash !== undefined) { 39 | nonce++ 40 | actualSent++ 41 | console.log(actualSent, ' # ', txHash) 42 | } 43 | } 44 | } catch (e) { 45 | console.log(e) 46 | } 47 | } 48 | main() 49 | -------------------------------------------------------------------------------- /oracle/scripts/getValidatorStartBlocks.js: -------------------------------------------------------------------------------- 1 | require('../env') 2 | 3 | const { BRIDGE_VALIDATORS_ABI } = require('../../commons') 4 | const { home, foreign } = require('../config/base.config') 5 | 6 | async function getStartBlock(bridgeContract, web3) { 7 | try { 8 | const deployedAtBlock = await bridgeContract.methods.deployedAtBlock().call() 9 | 10 | const validatorContractAddress = await bridgeContract.methods.validatorContract().call() 11 | const validatorContract = new web3.eth.Contract(BRIDGE_VALIDATORS_ABI, validatorContractAddress) 12 | 13 | const validatorDeployedAtBlock = await validatorContract.methods.deployedAtBlock().call() 14 | 15 | const validatorAddedEvents = await validatorContract.getPastEvents('ValidatorAdded', { 16 | fromBlock: validatorDeployedAtBlock, 17 | filter: { validator: process.env.ORACLE_VALIDATOR_ADDRESS } 18 | }) 19 | 20 | return validatorAddedEvents.length ? validatorAddedEvents[0].blockNumber : deployedAtBlock 21 | } catch (e) { 22 | return 0 23 | } 24 | } 25 | 26 | async function main() { 27 | const homeStartBlock = await getStartBlock(home.bridgeContract, home.web3) 28 | const foreignStartBlock = await getStartBlock(foreign.bridgeContract, foreign.web3) 29 | const result = { 30 | homeStartBlock, 31 | foreignStartBlock 32 | } 33 | console.log(JSON.stringify(result)) 34 | return result 35 | } 36 | 37 | main() 38 | 39 | module.exports = main 40 | -------------------------------------------------------------------------------- /oracle/scripts/privateKeyToAddress.js: -------------------------------------------------------------------------------- 1 | require('../env') 2 | const { privateKeyToAddress } = require('../src/utils/utils') 3 | const { EXIT_CODES } = require('../src/utils/constants') 4 | 5 | const privateKey = process.env.ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY 6 | 7 | if (!privateKey) { 8 | console.error('Environment variable ORACLE_VALIDATOR_ADDRESS_PRIVATE_KEY is not set') 9 | process.exit(EXIT_CODES.GENERAL_ERROR) 10 | } 11 | 12 | const address = privateKeyToAddress(privateKey) 13 | 14 | console.log(address) 15 | -------------------------------------------------------------------------------- /oracle/scripts/resetLastBlock.js: -------------------------------------------------------------------------------- 1 | require('../env') 2 | const Redis = require('ioredis') 3 | const { id } = require('../config/base.config') 4 | const { EXIT_CODES } = require('../src/utils/constants') 5 | 6 | const redis = new Redis(process.env.ORACLE_REDIS_URL) 7 | 8 | redis.on('error', () => { 9 | logError('Error: Cannot connect to redis') 10 | }) 11 | 12 | if (process.argv.length < 4) { 13 | logError( 14 | 'Please provide process key and new block value. Example:' + 15 | '\n signature-request 12345 ' + 16 | '\n collected-signatures 12345 ' + 17 | '\n affirmation-request 12345' 18 | ) 19 | } 20 | 21 | function logError(message) { 22 | console.log(message) 23 | process.exit(EXIT_CODES.GENERAL_ERROR) 24 | } 25 | 26 | function getRedisKey(name) { 27 | return `${id}-${name}:lastProcessedBlock` 28 | } 29 | 30 | async function main() { 31 | try { 32 | const processName = process.argv[2] 33 | const rawBlockValue = process.argv[3] 34 | 35 | const newBlockValue = Number(rawBlockValue) 36 | if (!Number.isInteger(newBlockValue)) { 37 | logError('Expecting new block value to be an integer!') 38 | } 39 | 40 | const lastBlockRedisKey = getRedisKey(processName) 41 | 42 | const value = await redis.get(lastBlockRedisKey) 43 | 44 | if (!value) { 45 | logError( 46 | 'Error: Process key not found on redis. Please provide one of the following:' + 47 | '\n signature-request' + 48 | '\n collected-signatures' + 49 | '\n affirmation-request' 50 | ) 51 | } 52 | 53 | await redis.set(lastBlockRedisKey, newBlockValue) 54 | 55 | console.log(`${processName} last block updated to ${newBlockValue}`) 56 | 57 | redis.disconnect() 58 | } catch (e) { 59 | console.log(e) 60 | } 61 | } 62 | 63 | main() 64 | -------------------------------------------------------------------------------- /oracle/scripts/start-worker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o pipefail 4 | 5 | WORKERS_DIR="src/" 6 | LOGS_DIR="logs/" 7 | 8 | WORKER="${WORKERS_DIR}${1}.js" 9 | CONFIG="${2}.config.js" 10 | LOG="${LOGS_DIR}${2}.txt" 11 | TX_HASH=${@:3} 12 | 13 | if [ "${NODE_ENV}" = "production" ]; then 14 | exec node "${WORKER}" "${CONFIG}" $TX_HASH 15 | else 16 | node "${WORKER}" "${CONFIG}" $TX_HASH | tee -a "${LOG}" | pino-pretty 17 | fi 18 | -------------------------------------------------------------------------------- /oracle/scripts/utils/utils.js: -------------------------------------------------------------------------------- 1 | const { fromWei } = require('web3').utils 2 | 3 | async function getMinPerTxLimit(bridge) { 4 | const minPerTx = await bridge.methods.minPerTx().call() 5 | return fromWei(minPerTx) 6 | } 7 | 8 | async function isValidAmount(amount, bridge) { 9 | const minLimit = await getMinPerTxLimit(bridge) 10 | if (amount < minLimit) { 11 | throw new Error(`The amount per Tx ${amount} should be at least ${minLimit}`) 12 | } 13 | } 14 | 15 | module.exports = { 16 | getMinPerTxLimit, 17 | isValidAmount 18 | } 19 | -------------------------------------------------------------------------------- /oracle/src/events/processAMBInformationRequests/calls/ethBlockNumber.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'eth_blockNumber()': async (web3, _, block) => [true, web3.eth.abi.encodeParameter('uint256', block.number)] 3 | } 4 | -------------------------------------------------------------------------------- /oracle/src/events/processAMBInformationRequests/calls/ethGetBalance.js: -------------------------------------------------------------------------------- 1 | const { toBN } = require('web3').utils 2 | 3 | const { ASYNC_CALL_ERRORS } = require('../../../utils/constants') 4 | 5 | async function call(web3, data, foreignBlock) { 6 | const address = web3.eth.abi.decodeParameter('address', data) 7 | 8 | const balance = await web3.eth.getBalance(address, foreignBlock.number) 9 | 10 | return [true, web3.eth.abi.encodeParameter('uint256', balance)] 11 | } 12 | 13 | async function callArchive(web3, data, foreignBlock) { 14 | const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data) 15 | 16 | if (toBN(blockNumber).gt(toBN(foreignBlock.number))) { 17 | return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE] 18 | } 19 | 20 | const balance = await web3.eth.getBalance(address, blockNumber) 21 | 22 | return [true, web3.eth.abi.encodeParameter('uint256', balance)] 23 | } 24 | 25 | module.exports = { 26 | 'eth_getBalance(address)': call, 27 | 'eth_getBalance(address,uint256)': callArchive 28 | } 29 | -------------------------------------------------------------------------------- /oracle/src/events/processAMBInformationRequests/calls/ethGetBlockByHash.js: -------------------------------------------------------------------------------- 1 | const { ASYNC_CALL_ERRORS } = require('../../../utils/constants') 2 | const { serializeBlock } = require('./serializers') 3 | 4 | async function call(web3, data, foreignBlock) { 5 | const blockHash = web3.eth.abi.decodeParameter('bytes32', data) 6 | 7 | const block = await web3.eth.getBlock(blockHash) 8 | 9 | if (block === null || block.number > foreignBlock.number) { 10 | return [false, ASYNC_CALL_ERRORS.NOT_FOUND] 11 | } 12 | 13 | return [true, serializeBlock(web3, block)] 14 | } 15 | 16 | module.exports = { 17 | 'eth_getBlockByHash(bytes32)': call 18 | } 19 | -------------------------------------------------------------------------------- /oracle/src/events/processAMBInformationRequests/calls/ethGetBlockByNumber.js: -------------------------------------------------------------------------------- 1 | const { toBN } = require('web3').utils 2 | 3 | const { ASYNC_CALL_ERRORS } = require('../../../utils/constants') 4 | const { serializeBlock } = require('./serializers') 5 | 6 | async function call(web3, data, foreignBlock) { 7 | const blockNumber = web3.eth.abi.decodeParameter('uint256', data) 8 | 9 | if (toBN(blockNumber).gt(toBN(foreignBlock.number))) { 10 | return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE] 11 | } 12 | 13 | const block = await web3.eth.getBlock(blockNumber) 14 | 15 | return [true, serializeBlock(web3, block)] 16 | } 17 | 18 | module.exports = { 19 | 'eth_getBlockByNumber()': async (web3, _, block) => [true, serializeBlock(web3, block)], 20 | 'eth_getBlockByNumber(uint256)': call 21 | } 22 | -------------------------------------------------------------------------------- /oracle/src/events/processAMBInformationRequests/calls/ethGetStorageAt.js: -------------------------------------------------------------------------------- 1 | const { toBN } = require('web3').utils 2 | 3 | const { ASYNC_CALL_ERRORS } = require('../../../utils/constants') 4 | 5 | async function call(web3, data, foreignBlock) { 6 | const { 0: address, 1: slot } = web3.eth.abi.decodeParameters(['address', 'bytes32'], data) 7 | 8 | const value = await web3.eth.getStorageAt(address, slot, foreignBlock.number) 9 | 10 | return [true, web3.eth.abi.encodeParameter('bytes32', value)] 11 | } 12 | 13 | async function callArchive(web3, data, foreignBlock) { 14 | const { 0: address, 1: slot, 2: blockNumber } = web3.eth.abi.decodeParameters(['address', 'bytes32', 'uint256'], data) 15 | 16 | if (toBN(blockNumber).gt(toBN(foreignBlock.number))) { 17 | return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE] 18 | } 19 | 20 | const value = await web3.eth.getStorageAt(address, slot, blockNumber) 21 | 22 | return [true, web3.eth.abi.encodeParameter('bytes32', value)] 23 | } 24 | 25 | module.exports = { 26 | 'eth_getStorageAt(address,bytes32)': call, 27 | 'eth_getStorageAt(address,bytes32,uint256)': callArchive 28 | } 29 | -------------------------------------------------------------------------------- /oracle/src/events/processAMBInformationRequests/calls/ethGetTransactionByHash.js: -------------------------------------------------------------------------------- 1 | const { ASYNC_CALL_ERRORS } = require('../../../utils/constants') 2 | const { serializeTx } = require('./serializers') 3 | 4 | async function call(web3, data, foreignBlock) { 5 | const hash = web3.eth.abi.decodeParameter('bytes32', data) 6 | 7 | const tx = await web3.eth.getTransaction(hash) 8 | 9 | if (tx === null || tx.blockNumber > foreignBlock.number) { 10 | return [false, ASYNC_CALL_ERRORS.NOT_FOUND] 11 | } 12 | 13 | return [true, serializeTx(web3, tx)] 14 | } 15 | 16 | module.exports = { 17 | 'eth_getTransactionByHash(bytes32)': call 18 | } 19 | -------------------------------------------------------------------------------- /oracle/src/events/processAMBInformationRequests/calls/ethGetTransactionCount.js: -------------------------------------------------------------------------------- 1 | const { toBN } = require('web3').utils 2 | 3 | const { ASYNC_CALL_ERRORS } = require('../../../utils/constants') 4 | 5 | async function call(web3, data, foreignBlock) { 6 | const address = web3.eth.abi.decodeParameter('address', data) 7 | 8 | const nonce = await web3.eth.getTransactionCount(address, foreignBlock.number) 9 | 10 | return [true, web3.eth.abi.encodeParameter('uint256', nonce)] 11 | } 12 | 13 | async function callArchive(web3, data, foreignBlock) { 14 | const { 0: address, 1: blockNumber } = web3.eth.abi.decodeParameters(['address', 'uint256'], data) 15 | 16 | if (toBN(blockNumber).gt(toBN(foreignBlock.number))) { 17 | return [false, ASYNC_CALL_ERRORS.BLOCK_IS_IN_THE_FUTURE] 18 | } 19 | 20 | const nonce = await web3.eth.getTransactionCount(address, blockNumber) 21 | 22 | return [true, web3.eth.abi.encodeParameter('uint256', nonce)] 23 | } 24 | 25 | module.exports = { 26 | 'eth_getTransactionCount(address)': call, 27 | 'eth_getTransactionCount(address,uint256)': callArchive 28 | } 29 | -------------------------------------------------------------------------------- /oracle/src/events/processAMBInformationRequests/calls/ethGetTransactionReceipt.js: -------------------------------------------------------------------------------- 1 | const { ASYNC_CALL_ERRORS } = require('../../../utils/constants') 2 | const { serializeReceipt } = require('./serializers') 3 | 4 | async function call(web3, data, foreignBlock) { 5 | const hash = web3.eth.abi.decodeParameter('bytes32', data) 6 | 7 | const receipt = await web3.eth.getTransactionReceipt(hash) 8 | 9 | if (receipt === null || receipt.blockNumber > foreignBlock.number) { 10 | return [false, ASYNC_CALL_ERRORS.NOT_FOUND] 11 | } 12 | 13 | return [true, serializeReceipt(web3, receipt)] 14 | } 15 | 16 | module.exports = { 17 | 'eth_getTransactionReceipt(bytes32)': call 18 | } 19 | -------------------------------------------------------------------------------- /oracle/src/services/RedundantHttpListProvider.js: -------------------------------------------------------------------------------- 1 | const promiseRetry = require('promise-retry') 2 | const { promiseAny } = require('../utils/utils') 3 | const { defaultOptions, HttpListProviderError, send } = require('./HttpListProvider') 4 | const { onInjected } = require('./injectedLogger') 5 | 6 | function RedundantHttpListProvider(urls, options = {}) { 7 | if (!(this instanceof RedundantHttpListProvider)) { 8 | return new RedundantHttpListProvider(urls) 9 | } 10 | 11 | if (!urls || !urls.length) { 12 | throw new TypeError(`Invalid URLs: '${urls}'`) 13 | } 14 | 15 | this.urls = urls 16 | this.options = { ...defaultOptions, ...options } 17 | onInjected(logger => { 18 | this.logger = logger.child({ module: `RedundantHttpListProvider:${this.options.name}` }) 19 | }) 20 | } 21 | 22 | RedundantHttpListProvider.prototype.send = async function send(payload, callback) { 23 | try { 24 | const result = await promiseRetry(retry => { 25 | return trySend(payload, this.urls, this.options).catch(retry) 26 | }, this.options.retry) 27 | callback(null, result) 28 | } catch (e) { 29 | callback(e) 30 | } 31 | } 32 | 33 | async function trySend(payload, urls, options) { 34 | try { 35 | return await promiseAny(urls.map(url => send(url, payload, options))) 36 | } catch (errors) { 37 | throw new HttpListProviderError('Request failed for all urls', errors) 38 | } 39 | } 40 | 41 | module.exports = { 42 | RedundantHttpListProvider 43 | } 44 | -------------------------------------------------------------------------------- /oracle/src/services/SafeEthLogsProvider.js: -------------------------------------------------------------------------------- 1 | const { hexToNumber, isHexStrict } = require('web3').utils 2 | 3 | function SafeEthLogsProvider(provider) { 4 | const oldSend = provider.send.bind(provider) 5 | const newSend = function(payload, callback) { 6 | if (payload.method === 'eth_getLogs' && isHexStrict(payload.params[0].toBlock)) { 7 | this.logger.debug('Modifying eth_getLogs request to include batch eth_blockNumber request') 8 | 9 | const newPayload = [payload, { jsonrpc: '2.0', id: payload.id + 1, method: 'eth_blockNumber', params: [] }] 10 | oldSend(newPayload, (err, res) => { 11 | if (err) { 12 | callback(err, null) 13 | } else { 14 | const rawLogs = res.find(({ id }) => id === payload.id) 15 | const rawBlockNumber = res.find(({ id }) => id === payload.id + 1) 16 | const blockNumber = hexToNumber(rawBlockNumber.result) 17 | const toBlock = hexToNumber(payload.params[0].toBlock) 18 | 19 | if (blockNumber < toBlock) { 20 | this.logger.warn({ toBlock, blockNumber }, 'Returned block number is less than the specified toBlock') 21 | callback(new Error('block number too low'), null) 22 | } else { 23 | callback(null, rawLogs) 24 | } 25 | } 26 | }) 27 | } else { 28 | oldSend(payload, callback) 29 | } 30 | } 31 | provider.send = newSend.bind(provider) 32 | return provider 33 | } 34 | 35 | module.exports = { 36 | SafeEthLogsProvider 37 | } 38 | -------------------------------------------------------------------------------- /oracle/src/services/injectedLogger.js: -------------------------------------------------------------------------------- 1 | // workaround to avoid circular dependencies in module imports 2 | // e.g. logger -> config -> web3 -> provider -> logger 3 | // transforms to the following import chain 4 | // logger -> config -> web3 -> provider -> injectedLogger 5 | // logger -> injectedLogger 6 | 7 | let logger 8 | 9 | const callbacks = [] 10 | 11 | function onInjected(cb) { 12 | if (logger) { 13 | cb(logger) 14 | } else { 15 | callbacks.push(cb) 16 | } 17 | } 18 | 19 | function setLogger(newLogger) { 20 | logger = newLogger 21 | callbacks.forEach(cb => cb(logger)) 22 | } 23 | 24 | module.exports = { 25 | onInjected, 26 | setLogger 27 | } 28 | -------------------------------------------------------------------------------- /oracle/src/services/logger.js: -------------------------------------------------------------------------------- 1 | const pino = require('pino') 2 | const path = require('path') 3 | 4 | const { setLogger } = require('./injectedLogger') 5 | 6 | const config = process.env.NODE_ENV !== 'test' ? require(path.join('../../config/', process.argv[2])) : {} 7 | 8 | const logger = pino({ 9 | enabled: process.env.NODE_ENV !== 'test', 10 | name: config.name, 11 | level: process.env.ORACLE_LOG_LEVEL || 'debug', 12 | base: { 13 | validator: config.validatorAddress 14 | } 15 | }) 16 | 17 | setLogger(logger) 18 | 19 | module.exports = logger 20 | -------------------------------------------------------------------------------- /oracle/src/services/redisClient.js: -------------------------------------------------------------------------------- 1 | const Redis = require('ioredis') 2 | const logger = require('./logger') 3 | 4 | const redis = new Redis(process.env.ORACLE_REDIS_URL) 5 | 6 | redis.on('connect', () => { 7 | logger.info('Connected to redis') 8 | }) 9 | 10 | redis.on('error', () => { 11 | logger.error('Disconnected from redis') 12 | }) 13 | 14 | module.exports = { 15 | redis 16 | } 17 | -------------------------------------------------------------------------------- /oracle/src/services/shutdownState.js: -------------------------------------------------------------------------------- 1 | const { redis } = require('./redisClient') 2 | 3 | let isShutdown = false 4 | async function getShutdownFlag(logger, shutdownKey, force = false) { 5 | if (force) { 6 | logger.debug('Reading current shutdown state from the DB') 7 | isShutdown = (await redis.get(shutdownKey)) === 'true' 8 | logger.debug({ isShutdown }, 'Read shutdown state from the DB') 9 | } 10 | return isShutdown 11 | } 12 | 13 | async function setShutdownFlag(logger, shutdownKey, value) { 14 | logger.info({ isShutdown: value }, 'Updating current shutdown state in the DB') 15 | isShutdown = value 16 | await redis.set(shutdownKey, value) 17 | logger.debug('Updated state in the DB') 18 | } 19 | 20 | module.exports = { 21 | getShutdownFlag, 22 | setShutdownFlag 23 | } 24 | -------------------------------------------------------------------------------- /oracle/src/tx/sendTx.js: -------------------------------------------------------------------------------- 1 | async function sendTx(opts) { 2 | const { privateKey, data, nonce, gasPrice, gasPriceOptions, value, gasLimit, to, chainId, web3, mevOptions } = opts 3 | const gasOpts = gasPriceOptions || { gasPrice } 4 | const serializedTx = await web3.eth.accounts.signTransaction( 5 | { 6 | nonce: Number(nonce), 7 | chainId, 8 | to, 9 | data, 10 | value, 11 | gas: gasLimit, 12 | ...gasOpts 13 | }, 14 | privateKey 15 | ) 16 | 17 | if (!mevOptions) { 18 | return new Promise((res, rej) => 19 | web3.eth 20 | .sendSignedTransaction(serializedTx.rawTransaction) 21 | .once('transactionHash', res) 22 | .once('error', rej) 23 | ) 24 | } 25 | 26 | mevOptions.logger.debug( 27 | { rawTx: serializedTx.rawTransaction, txHash: serializedTx.transactionHash }, 28 | 'Signed MEV helper transaction' 29 | ) 30 | 31 | for (let blockNumber = mevOptions.fromBlock; blockNumber <= mevOptions.toBlock; blockNumber++) { 32 | mevOptions.logger.debug({ txHash: serializedTx.transactionHash, blockNumber }, 'Sending MEV bundle transaction') 33 | await mevOptions.provider.sendRawBundle([serializedTx.rawTransaction], blockNumber) 34 | } 35 | return Promise.resolve(serializedTx.transactionHash) 36 | } 37 | 38 | module.exports = { 39 | sendTx 40 | } 41 | -------------------------------------------------------------------------------- /oracle/src/utils/errors.js: -------------------------------------------------------------------------------- 1 | class AlreadyProcessedError extends Error {} 2 | class AlreadySignedError extends Error {} 3 | class IncompatibleContractError extends Error {} 4 | class InvalidValidatorError extends Error {} 5 | 6 | module.exports = { 7 | AlreadyProcessedError, 8 | AlreadySignedError, 9 | IncompatibleContractError, 10 | InvalidValidatorError 11 | } 12 | -------------------------------------------------------------------------------- /oracle/src/utils/mev.js: -------------------------------------------------------------------------------- 1 | const MEV_HELPER_ABI = [ 2 | { 3 | constant: false, 4 | inputs: [ 5 | { 6 | name: '_data', 7 | type: 'bytes' 8 | } 9 | ], 10 | name: 'execute', 11 | outputs: [], 12 | payable: false, 13 | stateMutability: 'nonpayable', 14 | type: 'function' 15 | }, 16 | { 17 | constant: false, 18 | inputs: [ 19 | { 20 | name: '_gasPrice', 21 | type: 'uint256' 22 | }, 23 | { 24 | name: '_data', 25 | type: 'bytes' 26 | } 27 | ], 28 | name: 'estimateProfit', 29 | outputs: [ 30 | { 31 | name: '', 32 | type: 'uint256' 33 | } 34 | ], 35 | payable: true, 36 | stateMutability: 'nonpayable', 37 | type: 'function' 38 | } 39 | ] 40 | 41 | module.exports = { 42 | MEV_HELPER_ABI 43 | } 44 | -------------------------------------------------------------------------------- /oracle/src/utils/tokenState.js: -------------------------------------------------------------------------------- 1 | async function getTokensState(bridgeContract, logger) { 2 | const context = {} 3 | try { 4 | logger.debug('Getting bridgeable token address') 5 | context.bridgeableTokenAddress = await bridgeContract.methods.erc20token().call() 6 | logger.debug({ address: context.bridgeableTokenAddress }, 'Token address obtained') 7 | } catch (e) { 8 | throw new Error(`Bridgeable token address cannot be obtained`) 9 | } 10 | 11 | return context 12 | } 13 | 14 | module.exports = { 15 | getTokensState 16 | } 17 | -------------------------------------------------------------------------------- /oracle/src/utils/tryEach.js: -------------------------------------------------------------------------------- 1 | module.exports = async (array, f) => { 2 | const errors = [] 3 | 4 | for (let i = 0; i < array.length; i++) { 5 | try { 6 | const res = await f(array[i]) 7 | return [res, i] 8 | } catch (e) { 9 | errors.push(e) 10 | } 11 | } 12 | 13 | return Promise.reject(errors) 14 | } 15 | -------------------------------------------------------------------------------- /oracle/test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../.eslintrc", 3 | "globals": { 4 | "describe": false, 5 | "it": false, 6 | "beforeEach": false, 7 | "afterEach": false 8 | }, 9 | "rules": { 10 | "node/no-unpublished-require": "off" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /oracle/test/test.env: -------------------------------------------------------------------------------- 1 | COMMON_HOME_RPC_URL=http://example.com 2 | COMMON_FOREIGN_RPC_URL=http://example.com 3 | ORACLE_QUEUE_URL=http://example.com 4 | -------------------------------------------------------------------------------- /parity/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM parity/parity:v2.3.3 2 | 3 | WORKDIR /stuff 4 | 5 | COPY . . 6 | 7 | CMD ["--chain", "chain.json", "--network-id", "77", "--jsonrpc-apis", "all", "--jsonrpc-interface", "all", "--jsonrpc-cors", "all", "--jsonrpc-hosts", "all"] 8 | -------------------------------------------------------------------------------- /parity/Dockerfile-foreign: -------------------------------------------------------------------------------- 1 | FROM parity/parity:v2.3.3 2 | 3 | WORKDIR /stuff 4 | 5 | COPY . . 6 | 7 | CMD ["--chain", "chain-foreign.json", "--network-id", "42", "--jsonrpc-apis", "all", "--jsonrpc-interface", "all", "--jsonrpc-cors", "all", "--jsonrpc-hosts", "all"] 8 | -------------------------------------------------------------------------------- /parity/foreign_mocks/cDAIMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | interface IERC20 { 4 | function transferFrom(address from,address to,uint256 value) external; 5 | function transfer(address to,uint256 value) external; 6 | } 7 | 8 | contract cDaiMock { 9 | IERC20 daiToken; 10 | 11 | event Transfer(address indexed from, address indexed to, uint amount); 12 | event Mint(address minter, uint mintAmount, uint mintTokens); 13 | event Redeem(address redeemer, uint redeemAmount, uint redeemTokens); 14 | 15 | 16 | function mint(uint256 mintAmount) external returns (uint256) { 17 | daiToken.transferFrom(msg.sender, address(this), mintAmount); 18 | 19 | emit Mint(msg.sender, mintAmount, mintAmount); 20 | emit Transfer(address(this), msg.sender, mintAmount); 21 | 22 | return 0; 23 | } 24 | 25 | function redeemUnderlying(uint256 redeemAmount) external returns (uint256) { 26 | daiToken.transfer(msg.sender, redeemAmount); 27 | 28 | emit Transfer(msg.sender, address(this), redeemAmount); 29 | emit Redeem(msg.sender, redeemAmount, redeemAmount); 30 | 31 | return 0; 32 | } 33 | } 34 | --------------------------------------------------------------------------------