├── .circleci ├── ci_publish.sh └── config.yml ├── .eslintignore ├── .eslintrc ├── .gitignore ├── AUTHORS ├── CODEOWNERS ├── Dockerfile ├── LICENSE ├── README.md ├── browserify.js ├── docs ├── assets │ ├── anchor.js │ ├── bass-addons.css │ ├── bass.css │ ├── fonts │ │ ├── EOT │ │ │ ├── SourceCodePro-Bold.eot │ │ │ └── SourceCodePro-Regular.eot │ │ ├── LICENSE.txt │ │ ├── OTF │ │ │ ├── SourceCodePro-Bold.otf │ │ │ └── SourceCodePro-Regular.otf │ │ ├── TTF │ │ │ ├── SourceCodePro-Bold.ttf │ │ │ └── SourceCodePro-Regular.ttf │ │ ├── WOFF │ │ │ ├── OTF │ │ │ │ ├── SourceCodePro-Bold.otf.woff │ │ │ │ └── SourceCodePro-Regular.otf.woff │ │ │ └── TTF │ │ │ │ ├── SourceCodePro-Bold.ttf.woff │ │ │ │ └── SourceCodePro-Regular.ttf.woff │ │ ├── WOFF2 │ │ │ ├── OTF │ │ │ │ ├── SourceCodePro-Bold.otf.woff2 │ │ │ │ └── SourceCodePro-Regular.otf.woff2 │ │ │ └── TTF │ │ │ │ ├── SourceCodePro-Bold.ttf.woff2 │ │ │ │ └── SourceCodePro-Regular.ttf.woff2 │ │ └── source-code-pro.css │ ├── github.css │ ├── site.js │ ├── split.css │ ├── split.js │ └── style.css └── index.html ├── documentation.yml ├── env.docker.example ├── examples ├── config.js ├── deposit-erc20.js ├── deposit-eth.js ├── env.example ├── exit-erc20.js ├── exit-eth.js ├── inflight-exit-eth.js ├── merge-utxos.js ├── package-lock.json ├── package.json ├── parse-args.js ├── print-balance.js ├── print-utxos.js ├── process-exits.js ├── transaction-erc20.js ├── transaction-eth.js └── wait.js ├── integration-docs ├── signing-methods.md └── transactions.md ├── lerna.json ├── package-lock.json ├── package.json └── packages ├── browser-omg-js ├── package-lock.json └── package.json ├── integration-tests ├── .npmrc ├── README.md ├── ci-baseline-test.js ├── ci-ife-test.js ├── ci-min-smoke-test.js ├── cleanup-faucets.js ├── docker-compose.yml ├── helpers │ ├── childChainHelper.js │ ├── faucet.js │ └── rootChainHelper.js ├── package-lock.json ├── package.json ├── parallel-test.js ├── sequence-test.js ├── test-config.js ├── test │ ├── CORSHeaderTest.js │ ├── addTokenTest.js │ ├── amountTypes.js │ ├── challengeExitTest.js │ ├── challengeInFlightExitInputSpentTest.js │ ├── challengeInFlightExitOutputSpentTest.js │ ├── createSubmitTypedTransactionTest.js │ ├── createTransactionTest.js │ ├── decodeTxBytesTest.js │ ├── deleteNonPiggybackedInFlightExitTest.js │ ├── depositTest.js │ ├── exitWithoutWatcherTest.js │ ├── getExitQueueTest.js │ ├── inFlightExitChallengeResponseTest.js │ ├── inFlightExitChallengeTest.js │ ├── inFlightExitTest.js │ ├── mergeUtxoTest.js │ ├── metadataTest.js │ ├── simpleStartStandardExitTest.js │ ├── standardExitTest.js │ ├── transfer │ │ ├── mixCurrencyTransferTest.js │ │ ├── sendTransactionTest.js │ │ ├── simpleErc20TransferTest.js │ │ ├── simpleEthTransferTest.js │ │ └── transferWith4InputsAndOutputsTest.js │ └── transferTest.js └── tokens │ ├── contracts │ ├── ERC20.sol │ └── Migrations.sol │ ├── migrations │ ├── 1_initial_migration.js │ └── 2_deploy_contracts.js │ └── truffle.js ├── omg-js-childchain ├── package-lock.json ├── package.json ├── src │ ├── childchain.js │ ├── rpc │ │ └── rpcApi.js │ └── validators │ │ ├── helpers.js │ │ └── index.js └── test │ ├── createTransactionTest.js │ ├── getBalanceTest.js │ ├── getUtxotest.js │ ├── mock.js │ ├── rpcApiTest.js │ └── signTransactionTest.js ├── omg-js-rootchain ├── package-lock.json ├── package.json ├── src │ ├── contracts │ │ ├── Erc20Vault.json │ │ ├── EthVault.json │ │ ├── PaymentExitGame.json │ │ ├── PlasmaFramework.json │ │ └── PriorityQueue.json │ ├── merkle.js │ ├── rootchain.js │ ├── txUtils.js │ └── validators │ │ ├── helpers.js │ │ └── index.js └── test │ └── index.js ├── omg-js-util ├── package-lock.json ├── package.json ├── src │ ├── address.js │ ├── docTypes.js │ ├── ethErrorReason.js │ ├── getErc20Balance.js │ ├── hexPrefix.js │ ├── hexToBytes.js │ ├── index.js │ ├── sign.js │ ├── signHash.js │ ├── transaction.js │ ├── typedData.js │ ├── utxo.js │ ├── validators │ │ ├── helpers.js │ │ └── index.js │ ├── waitForChildchainBalance.js │ └── waitForRootchainTransaction.js └── test │ ├── createTransactionBodyTest.js │ ├── decodeTransactionTest.js │ ├── encodeMetadataTest.js │ ├── encodeTransactionTest.js │ ├── encodeUtxoTest.js │ ├── signHashTest.js │ ├── utxoTest.js │ └── validateTransactionTest.js ├── omg-js ├── package-lock.json ├── package.json └── src │ └── index.js └── react-native-omg-js ├── hack.sh ├── index.js ├── package-lock.json ├── package.json ├── postinstall.sh └── shim.js /.circleci/ci_publish.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | GIT_COMMIT_SHA=$(git rev-parse --short=7 HEAD) 6 | TEST_RUNNER_TAG=gcr.io/omisego-development/omg-js-testrunner:$GIT_COMMIT_SHA 7 | 8 | docker build -t testrunner . 9 | docker tag testrunner $TEST_RUNNER_TAG 10 | docker push $TEST_RUNNER_TAG 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | docs 3 | 4 | packages/browser-omg-js 5 | packages/react-native-omg-js -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "standard", 4 | "plugin:mocha/recommended" 5 | ], 6 | "plugins": [ 7 | "mocha" 8 | ], 9 | "env": { 10 | "mocha": true 11 | }, 12 | "rules": { 13 | "mocha/no-hooks-for-single-case": 0, 14 | "mocha/max-top-level-suites": 0, 15 | "mocha/no-setup-in-describe": 0 16 | } 17 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .env 4 | .DS_Store 5 | 6 | # VS Code directories 7 | .vscode 8 | 9 | .idea 10 | 11 | build 12 | dist 13 | 14 | # ignore browserified build 15 | packages/browser-omg-js/omg.js 16 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | OmiseGo Pte Ltd -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @nicholasmueller @kevsul @jarindr 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12-alpine 2 | 3 | # Add other convenient dependency 4 | # For instace, we would like curl to trigger slack hook when using this docker to run test 5 | RUN apk update && apk add shadow git python g++ make curl 6 | 7 | COPY . /home/omg/ 8 | 9 | RUN useradd -ms /bin/bash omg 10 | RUN chown -R omg:omg /home/omg/ 11 | 12 | USER omg 13 | 14 | WORKDIR /home/omg/ 15 | 16 | # WARNING: omg-js has a postinstall hook that will only be working if not running as root 17 | # https://stackoverflow.com/questions/47748075/npm-postinstall-not-running-in-docker 18 | # Since we are running as user: `omg` so it will be fine here 19 | RUN npm install 20 | -------------------------------------------------------------------------------- /browserify.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const ChildChain = require('./packages/omg-js-childchain/src/childchain') 17 | const RootChain = require('./packages/omg-js-rootchain/src/rootchain') 18 | const OmgUtil = require('./packages/omg-js-util/src/index') 19 | global.ChildChain = ChildChain 20 | global.RootChain = RootChain 21 | global.OmgUtil = OmgUtil 22 | -------------------------------------------------------------------------------- /docs/assets/bass-addons.css: -------------------------------------------------------------------------------- 1 | .input { 2 | font-family: inherit; 3 | display: block; 4 | width: 100%; 5 | height: 2rem; 6 | padding: .5rem; 7 | margin-bottom: 1rem; 8 | border: 1px solid #ccc; 9 | font-size: .875rem; 10 | border-radius: 3px; 11 | box-sizing: border-box; 12 | } 13 | -------------------------------------------------------------------------------- /docs/assets/fonts/EOT/SourceCodePro-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/EOT/SourceCodePro-Bold.eot -------------------------------------------------------------------------------- /docs/assets/fonts/EOT/SourceCodePro-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/EOT/SourceCodePro-Regular.eot -------------------------------------------------------------------------------- /docs/assets/fonts/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /docs/assets/fonts/OTF/SourceCodePro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/OTF/SourceCodePro-Bold.otf -------------------------------------------------------------------------------- /docs/assets/fonts/OTF/SourceCodePro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/OTF/SourceCodePro-Regular.otf -------------------------------------------------------------------------------- /docs/assets/fonts/TTF/SourceCodePro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/TTF/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/source-code-pro.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family: 'Source Code Pro'; 3 | font-weight: 400; 4 | font-style: normal; 5 | font-stretch: normal; 6 | src: url('EOT/SourceCodePro-Regular.eot') format('embedded-opentype'), 7 | url('WOFF2/TTF/SourceCodePro-Regular.ttf.woff2') format('woff2'), 8 | url('WOFF/OTF/SourceCodePro-Regular.otf.woff') format('woff'), 9 | url('OTF/SourceCodePro-Regular.otf') format('opentype'), 10 | url('TTF/SourceCodePro-Regular.ttf') format('truetype'); 11 | } 12 | 13 | @font-face{ 14 | font-family: 'Source Code Pro'; 15 | font-weight: 700; 16 | font-style: normal; 17 | font-stretch: normal; 18 | src: url('EOT/SourceCodePro-Bold.eot') format('embedded-opentype'), 19 | url('WOFF2/TTF/SourceCodePro-Bold.ttf.woff2') format('woff2'), 20 | url('WOFF/OTF/SourceCodePro-Bold.otf.woff') format('woff'), 21 | url('OTF/SourceCodePro-Bold.otf') format('opentype'), 22 | url('TTF/SourceCodePro-Bold.ttf') format('truetype'); 23 | } 24 | -------------------------------------------------------------------------------- /docs/assets/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | color: #333; 12 | background: #f8f8f8; 13 | -webkit-text-size-adjust: none; 14 | } 15 | 16 | .hljs-comment, 17 | .diff .hljs-header, 18 | .hljs-javadoc { 19 | color: #998; 20 | font-style: italic; 21 | } 22 | 23 | .hljs-keyword, 24 | .css .rule .hljs-keyword, 25 | .hljs-winutils, 26 | .nginx .hljs-title, 27 | .hljs-subst, 28 | .hljs-request, 29 | .hljs-status { 30 | color: #1184CE; 31 | } 32 | 33 | .hljs-number, 34 | .hljs-hexcolor, 35 | .ruby .hljs-constant { 36 | color: #ed225d; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-tag .hljs-value, 41 | .hljs-phpdoc, 42 | .hljs-dartdoc, 43 | .tex .hljs-formula { 44 | color: #ed225d; 45 | } 46 | 47 | .hljs-title, 48 | .hljs-id, 49 | .scss .hljs-preprocessor { 50 | color: #900; 51 | font-weight: bold; 52 | } 53 | 54 | .hljs-list .hljs-keyword, 55 | .hljs-subst { 56 | font-weight: normal; 57 | } 58 | 59 | .hljs-class .hljs-title, 60 | .hljs-type, 61 | .vhdl .hljs-literal, 62 | .tex .hljs-command { 63 | color: #458; 64 | font-weight: bold; 65 | } 66 | 67 | .hljs-tag, 68 | .hljs-tag .hljs-title, 69 | .hljs-rules .hljs-property, 70 | .django .hljs-tag .hljs-keyword { 71 | color: #000080; 72 | font-weight: normal; 73 | } 74 | 75 | .hljs-attribute, 76 | .hljs-variable, 77 | .lisp .hljs-body { 78 | color: #008080; 79 | } 80 | 81 | .hljs-regexp { 82 | color: #009926; 83 | } 84 | 85 | .hljs-symbol, 86 | .ruby .hljs-symbol .hljs-string, 87 | .lisp .hljs-keyword, 88 | .clojure .hljs-keyword, 89 | .scheme .hljs-keyword, 90 | .tex .hljs-special, 91 | .hljs-prompt { 92 | color: #990073; 93 | } 94 | 95 | .hljs-built_in { 96 | color: #0086b3; 97 | } 98 | 99 | .hljs-preprocessor, 100 | .hljs-pragma, 101 | .hljs-pi, 102 | .hljs-doctype, 103 | .hljs-shebang, 104 | .hljs-cdata { 105 | color: #999; 106 | font-weight: bold; 107 | } 108 | 109 | .hljs-deletion { 110 | background: #fdd; 111 | } 112 | 113 | .hljs-addition { 114 | background: #dfd; 115 | } 116 | 117 | .diff .hljs-change { 118 | background: #0086b3; 119 | } 120 | 121 | .hljs-chunk { 122 | color: #aaa; 123 | } 124 | -------------------------------------------------------------------------------- /docs/assets/site.js: -------------------------------------------------------------------------------- 1 | /* global anchors */ 2 | 3 | // add anchor links to headers 4 | anchors.options.placement = 'left'; 5 | anchors.add('h3'); 6 | 7 | // Filter UI 8 | var tocElements = document.getElementById('toc').getElementsByTagName('li'); 9 | 10 | document.getElementById('filter-input').addEventListener('keyup', function(e) { 11 | var i, element, children; 12 | 13 | // enter key 14 | if (e.keyCode === 13) { 15 | // go to the first displayed item in the toc 16 | for (i = 0; i < tocElements.length; i++) { 17 | element = tocElements[i]; 18 | if (!element.classList.contains('display-none')) { 19 | location.replace(element.firstChild.href); 20 | return e.preventDefault(); 21 | } 22 | } 23 | } 24 | 25 | var match = function() { 26 | return true; 27 | }; 28 | 29 | var value = this.value.toLowerCase(); 30 | 31 | if (!value.match(/^\s*$/)) { 32 | match = function(element) { 33 | var html = element.firstChild.innerHTML; 34 | return html && html.toLowerCase().indexOf(value) !== -1; 35 | }; 36 | } 37 | 38 | for (i = 0; i < tocElements.length; i++) { 39 | element = tocElements[i]; 40 | children = Array.from(element.getElementsByTagName('li')); 41 | if (match(element) || children.some(match)) { 42 | element.classList.remove('display-none'); 43 | } else { 44 | element.classList.add('display-none'); 45 | } 46 | } 47 | }); 48 | 49 | var items = document.getElementsByClassName('toggle-sibling'); 50 | for (var j = 0; j < items.length; j++) { 51 | items[j].addEventListener('click', toggleSibling); 52 | } 53 | 54 | function toggleSibling() { 55 | var stepSibling = this.parentNode.getElementsByClassName('toggle-target')[0]; 56 | var icon = this.getElementsByClassName('icon')[0]; 57 | var klass = 'display-none'; 58 | if (stepSibling.classList.contains(klass)) { 59 | stepSibling.classList.remove(klass); 60 | icon.innerHTML = '▾'; 61 | } else { 62 | stepSibling.classList.add(klass); 63 | icon.innerHTML = '▸'; 64 | } 65 | } 66 | 67 | function showHashTarget(targetId) { 68 | if (targetId) { 69 | var hashTarget = document.getElementById(targetId); 70 | // new target is hidden 71 | if ( 72 | hashTarget && 73 | hashTarget.offsetHeight === 0 && 74 | hashTarget.parentNode.parentNode.classList.contains('display-none') 75 | ) { 76 | hashTarget.parentNode.parentNode.classList.remove('display-none'); 77 | } 78 | } 79 | } 80 | 81 | function scrollIntoView(targetId) { 82 | // Only scroll to element if we don't have a stored scroll position. 83 | if (targetId && !history.state) { 84 | var hashTarget = document.getElementById(targetId); 85 | if (hashTarget) { 86 | hashTarget.scrollIntoView(); 87 | } 88 | } 89 | } 90 | 91 | function gotoCurrentTarget() { 92 | showHashTarget(location.hash.substring(1)); 93 | scrollIntoView(location.hash.substring(1)); 94 | } 95 | 96 | window.addEventListener('hashchange', gotoCurrentTarget); 97 | gotoCurrentTarget(); 98 | 99 | var toclinks = document.getElementsByClassName('pre-open'); 100 | for (var k = 0; k < toclinks.length; k++) { 101 | toclinks[k].addEventListener('mousedown', preOpen, false); 102 | } 103 | 104 | function preOpen() { 105 | showHashTarget(this.hash.substring(1)); 106 | } 107 | 108 | var split_left = document.querySelector('#split-left'); 109 | var split_right = document.querySelector('#split-right'); 110 | var split_parent = split_left.parentNode; 111 | var cw_with_sb = split_left.clientWidth; 112 | split_left.style.overflow = 'hidden'; 113 | var cw_without_sb = split_left.clientWidth; 114 | split_left.style.overflow = ''; 115 | 116 | Split(['#split-left', '#split-right'], { 117 | elementStyle: function(dimension, size, gutterSize) { 118 | return { 119 | 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' 120 | }; 121 | }, 122 | gutterStyle: function(dimension, gutterSize) { 123 | return { 124 | 'flex-basis': gutterSize + 'px' 125 | }; 126 | }, 127 | gutterSize: 20, 128 | sizes: [33, 67] 129 | }); 130 | 131 | // Chrome doesn't remember scroll position properly so do it ourselves. 132 | // Also works on Firefox and Edge. 133 | 134 | function updateState() { 135 | history.replaceState( 136 | { 137 | left_top: split_left.scrollTop, 138 | right_top: split_right.scrollTop 139 | }, 140 | document.title 141 | ); 142 | } 143 | 144 | function loadState(ev) { 145 | if (ev) { 146 | // Edge doesn't replace change history.state on popstate. 147 | history.replaceState(ev.state, document.title); 148 | } 149 | if (history.state) { 150 | split_left.scrollTop = history.state.left_top; 151 | split_right.scrollTop = history.state.right_top; 152 | } 153 | } 154 | 155 | window.addEventListener('load', function() { 156 | // Restore after Firefox scrolls to hash. 157 | setTimeout(function() { 158 | loadState(); 159 | // Update with initial scroll position. 160 | updateState(); 161 | // Update scroll positions only after we've loaded because Firefox 162 | // emits an initial scroll event with 0. 163 | split_left.addEventListener('scroll', updateState); 164 | split_right.addEventListener('scroll', updateState); 165 | }, 1); 166 | }); 167 | 168 | window.addEventListener('popstate', loadState); 169 | -------------------------------------------------------------------------------- /docs/assets/split.css: -------------------------------------------------------------------------------- 1 | .gutter { 2 | background-color: #f5f5f5; 3 | background-repeat: no-repeat; 4 | background-position: 50%; 5 | } 6 | 7 | .gutter.gutter-vertical { 8 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII='); 9 | cursor: ns-resize; 10 | } 11 | 12 | .gutter.gutter-horizontal { 13 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=='); 14 | cursor: ew-resize; 15 | } 16 | -------------------------------------------------------------------------------- /docs/assets/style.css: -------------------------------------------------------------------------------- 1 | .documentation { 2 | font-family: Helvetica, sans-serif; 3 | color: #666; 4 | line-height: 1.5; 5 | background: #f5f5f5; 6 | } 7 | 8 | .black { 9 | color: #666; 10 | } 11 | 12 | .bg-white { 13 | background-color: #fff; 14 | } 15 | 16 | h4 { 17 | margin: 20px 0 10px 0; 18 | } 19 | 20 | .documentation h3 { 21 | color: #000; 22 | } 23 | 24 | .border-bottom { 25 | border-color: #ddd; 26 | } 27 | 28 | a { 29 | color: #1184ce; 30 | text-decoration: none; 31 | } 32 | 33 | .documentation a[href]:hover { 34 | text-decoration: underline; 35 | } 36 | 37 | a:hover { 38 | cursor: pointer; 39 | } 40 | 41 | .py1-ul li { 42 | padding: 5px 0; 43 | } 44 | 45 | .max-height-100 { 46 | max-height: 100%; 47 | } 48 | 49 | .height-viewport-100 { 50 | height: 100vh; 51 | } 52 | 53 | section:target h3 { 54 | font-weight: 700; 55 | } 56 | 57 | .documentation td, 58 | .documentation th { 59 | padding: 0.25rem 0.25rem; 60 | } 61 | 62 | h1:hover .anchorjs-link, 63 | h2:hover .anchorjs-link, 64 | h3:hover .anchorjs-link, 65 | h4:hover .anchorjs-link { 66 | opacity: 1; 67 | } 68 | 69 | .fix-3 { 70 | width: 25%; 71 | max-width: 244px; 72 | } 73 | 74 | .fix-3 { 75 | width: 25%; 76 | max-width: 244px; 77 | } 78 | 79 | @media (min-width: 52em) { 80 | .fix-margin-3 { 81 | margin-left: 25%; 82 | } 83 | } 84 | 85 | .pre, 86 | pre, 87 | code, 88 | .code { 89 | font-family: Source Code Pro, Menlo, Consolas, Liberation Mono, monospace; 90 | font-size: 14px; 91 | } 92 | 93 | .fill-light { 94 | background: #f9f9f9; 95 | } 96 | 97 | .width2 { 98 | width: 1rem; 99 | } 100 | 101 | .input { 102 | font-family: inherit; 103 | display: block; 104 | width: 100%; 105 | height: 2rem; 106 | padding: 0.5rem; 107 | margin-bottom: 1rem; 108 | border: 1px solid #ccc; 109 | font-size: 0.875rem; 110 | border-radius: 3px; 111 | box-sizing: border-box; 112 | } 113 | 114 | table { 115 | border-collapse: collapse; 116 | } 117 | 118 | .prose table th, 119 | .prose table td { 120 | text-align: left; 121 | padding: 8px; 122 | border: 1px solid #ddd; 123 | } 124 | 125 | .prose table th:nth-child(1) { 126 | border-right: none; 127 | } 128 | .prose table th:nth-child(2) { 129 | border-left: none; 130 | } 131 | 132 | .prose table { 133 | border: 1px solid #ddd; 134 | } 135 | 136 | .prose-big { 137 | font-size: 18px; 138 | line-height: 30px; 139 | } 140 | 141 | .quiet { 142 | opacity: 0.7; 143 | } 144 | 145 | .minishadow { 146 | box-shadow: 2px 2px 10px #f3f3f3; 147 | } 148 | -------------------------------------------------------------------------------- /documentation.yml: -------------------------------------------------------------------------------- 1 | toc: 2 | - name: ChildChain 3 | children: 4 | - getUtxos 5 | - getBalance 6 | - getTransaction 7 | - getTransactions 8 | - getExitData 9 | - getChallengeData 10 | - getFees 11 | - createTransaction 12 | - signTypedData 13 | - submitTyped 14 | - signTransaction 15 | - buildSignedTransaction 16 | - submitTransaction 17 | - sendTransaction 18 | - mergeUtxos 19 | - status 20 | - inFlightExitGetInputChallengeData 21 | - inFlightExitGetOutputChallengeData 22 | - inFlightExitGetData 23 | - inFlightExitGetCompetitor 24 | - inFlightExitProveCanonical 25 | 26 | - name: RootChain 27 | children: 28 | - getExitTime 29 | - getExitQueue 30 | - getDepositExitData 31 | - approveToken 32 | - deposit 33 | - getStandardExitId 34 | - getInFlightExitId 35 | - getInFlightExitData 36 | - startStandardExit 37 | - challengeStandardExit 38 | - processExits 39 | - hasToken 40 | - addToken 41 | - startInFlightExit 42 | - piggybackInFlightExitOnOutput 43 | - piggybackInFlightExitOnInput 44 | - challengeInFlightExitNotCanonical 45 | - respondToNonCanonicalChallenge 46 | - challengeInFlightExitInputSpent 47 | - challengeInFlightExitOutputSpent 48 | - deleteNonPiggybackedInFlightExit 49 | 50 | - name: OMG Util 51 | children: 52 | - transaction 53 | - sign 54 | - hexToBytes 55 | - hexPrefix 56 | - ethErrorReason 57 | - getErc20Balance 58 | - waitForRootchainTransaction 59 | - waitForChildchainBalance 60 | 61 | - name: Types 62 | children: 63 | - Web3 64 | - TransactionReceipt 65 | - UTXO 66 | - Output 67 | - TransactionData 68 | - TransactionBody 69 | - DepositTransaction 70 | - BigNumber 71 | - TypedData 72 | - TransactionOptions 73 | - TransactionCallbacks 74 | - Balance 75 | - ExitData 76 | - Payment 77 | - Fee 78 | - FeeInfo 79 | -------------------------------------------------------------------------------- /env.docker.example: -------------------------------------------------------------------------------- 1 | ETH_NODE=http://localhost:8545 2 | WATCHER_URL=http://localhost:7534 3 | WATCHER_PROXY_URL= 4 | CHILDCHAIN_URL=http://localhost:9656 5 | PLASMAFRAMEWORK_CONTRACT_ADDRESS=0xc673e4ffcb8464faff908a6804fe0e635af0ea2f 6 | ERC20_CONTRACT_ADDRESS=0xedd7db3a2d8a93edc7f737048f9d84e6c9e40a41 7 | FUND_ACCOUNT=0x6de4b3b9c28e9c3e84c2b2d3a875c947a84de68d 8 | FUND_ACCOUNT_PRIVATEKEY=0xd885a307e35738f773d8c9c63c7a3f3977819274638d04aaf934a1e1158513ce 9 | MIN_AMOUNT_ETH_PER_TEST=1 10 | MIN_AMOUNT_ERC20_PER_TEST=10 11 | TOPUP_MULTIPLIER=3 12 | FAUCET_SALT= -------------------------------------------------------------------------------- /examples/config.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | require('dotenv').config() 18 | const { hexPrefix } = require('../packages/omg-js-util/src') 19 | 20 | const config = { 21 | eth_node: process.env.ETH_NODE, 22 | watcher_url: process.env.WATCHER_URL, 23 | watcher_proxy_url: process.env.WATCHER_PROXY_URL, 24 | plasmaframework_contract_address: hexPrefix(process.env.PLASMAFRAMEWORK_CONTRACT_ADDRESS), 25 | erc20_contract_address: process.env.ERC20_CONTRACT_ADDRESS ? hexPrefix(process.env.ERC20_CONTRACT_ADDRESS) : undefined, 26 | alice_eth_address: hexPrefix(process.env.ALICE_ETH_ADDRESS), 27 | alice_eth_address_private_key: hexPrefix(process.env.ALICE_ETH_ADDRESS_PRIVATE_KEY), 28 | bob_eth_address: hexPrefix(process.env.BOB_ETH_ADDRESS), 29 | bob_eth_address_private_key: hexPrefix(process.env.BOB_ETH_ADDRESS_PRIVATE_KEY), 30 | millis_to_wait_for_next_block: process.env.MILLIS_TO_WAIT_FOR_NEXT_BLOCK || 1000, 31 | blocks_to_wait_for_txn: process.env.BLOCKS_TO_WAIT_FOR_TXN || 20 32 | } 33 | 34 | module.exports = config 35 | -------------------------------------------------------------------------------- /examples/deposit-erc20.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const Web3 = require('web3') 18 | const RootChain = require('../packages/omg-js-rootchain/src/rootchain') 19 | const ChildChain = require('../packages/omg-js-childchain/src/childchain') 20 | const { getErc20Balance, waitForRootchainTransaction } = require('../packages/omg-js-util/src') 21 | const wait = require('./wait.js') 22 | const getFlags = require('./parse-args') 23 | 24 | const config = require('./config.js') 25 | 26 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node), null, { transactionConfirmationBlocks: 1 }) 27 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 28 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 29 | 30 | async function logBalances (address) { 31 | const rootchainERC20Balance = await getErc20Balance({ 32 | web3, 33 | address, 34 | erc20Address: config.erc20_contract_address 35 | }) 36 | const childchainBalanceArray = await childChain.getBalance(address) 37 | const erc20Object = childchainBalanceArray.find(i => i.currency.toLowerCase() === config.erc20_contract_address.toLowerCase()) 38 | const childchainERC20Balance = erc20Object ? erc20Object.amount : 0 39 | 40 | console.log(`Rootchain ERC20 balance: ${rootchainERC20Balance}`) 41 | console.log(`Childchain ERC20 balance: ${childchainERC20Balance}`) 42 | } 43 | 44 | async function depositErc20 () { 45 | try { 46 | const { owner, amount } = getFlags('owner', 'amount') 47 | const address = config[`${owner}_eth_address`] 48 | const pk = config[`${owner}_eth_address_private_key`] 49 | 50 | if (!config.erc20_contract_address) { 51 | throw Error('Please define an ERC20 contract address in your .env') 52 | } 53 | 54 | await logBalances(address) 55 | console.log('-----') 56 | 57 | console.log('Approving ERC20 for deposit...') 58 | const approveRes = await rootChain.approveToken({ 59 | erc20Address: config.erc20_contract_address, 60 | amount, 61 | txOptions: { 62 | from: address, 63 | privateKey: pk 64 | } 65 | }) 66 | console.log('ERC20 approved: ', approveRes.transactionHash) 67 | 68 | console.log(`Depositing ${amount} ERC20 from the rootchain to the childchain`) 69 | const transactionReceipt = await rootChain.deposit({ 70 | amount, 71 | currency: config.erc20_contract_address, 72 | txOptions: { 73 | from: address, 74 | privateKey: pk 75 | } 76 | }) 77 | console.log('Deposit successful: ', transactionReceipt.transactionHash) 78 | 79 | console.log('Waiting for transaction to be recorded by the watcher...') 80 | await waitForRootchainTransaction({ 81 | web3, 82 | transactionHash: transactionReceipt.transactionHash, 83 | checkIntervalMs: config.millis_to_wait_for_next_block, 84 | blocksToWait: config.blocks_to_wait_for_txn, 85 | onCountdown: (remaining) => console.log(`${remaining} blocks remaining before confirmation`) 86 | }) 87 | 88 | await wait.wait(5000) 89 | console.log('-----') 90 | await logBalances(address) 91 | } catch (error) { 92 | console.log('Error: ', error.message) 93 | } 94 | } 95 | 96 | depositErc20() 97 | -------------------------------------------------------------------------------- /examples/deposit-eth.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const BigNumber = require('bn.js') 18 | const Web3 = require('web3') 19 | const RootChain = require('../packages/omg-js-rootchain/src/rootchain') 20 | const ChildChain = require('../packages/omg-js-childchain/src/childchain') 21 | const { transaction, waitForRootchainTransaction } = require('../packages/omg-js-util/src') 22 | const wait = require('./wait.js') 23 | const getFlags = require('./parse-args') 24 | 25 | const config = require('./config.js') 26 | 27 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node), null, { transactionConfirmationBlocks: 1 }) 28 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 29 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 30 | 31 | async function logBalances (address) { 32 | const rootchainBalance = await web3.eth.getBalance(address) 33 | const childchainBalanceArray = await childChain.getBalance(address) 34 | const ethObject = childchainBalanceArray.find(i => i.currency === transaction.ETH_CURRENCY) 35 | const childchainETHBalance = ethObject 36 | ? `${web3.utils.fromWei(String(ethObject.amount))} ETH` 37 | : '0 ETH' 38 | 39 | console.log(`Rootchain ETH balance: ${web3.utils.fromWei(String(rootchainBalance), 'ether')} ETH`) 40 | console.log(`Childchain ETH balance: ${childchainETHBalance}`) 41 | } 42 | 43 | async function depositEth () { 44 | try { 45 | const { owner, amount } = getFlags('owner', 'amount') 46 | const depositAmount = new BigNumber(web3.utils.toWei(amount, 'ether')) 47 | const address = config[`${owner}_eth_address`] 48 | const pk = config[`${owner}_eth_address_private_key`] 49 | 50 | await logBalances(address) 51 | console.log('-----') 52 | 53 | console.log(`Depositing ${web3.utils.fromWei(depositAmount.toString(), 'ether')} ETH from the rootchain to the childchain`) 54 | const transactionReceipt = await rootChain.deposit({ 55 | amount: depositAmount, 56 | txOptions: { 57 | from: address, 58 | privateKey: pk 59 | } 60 | }) 61 | console.log('Deposit successful: ', transactionReceipt.transactionHash) 62 | 63 | console.log('Waiting for transaction to be recorded by the watcher...') 64 | await waitForRootchainTransaction({ 65 | web3, 66 | transactionHash: transactionReceipt.transactionHash, 67 | checkIntervalMs: config.millis_to_wait_for_next_block, 68 | blocksToWait: config.blocks_to_wait_for_txn, 69 | onCountdown: (remaining) => console.log(`${remaining} blocks remaining before confirmation`) 70 | }) 71 | 72 | await wait.wait(5000) 73 | console.log('-----') 74 | await logBalances(address) 75 | } catch (error) { 76 | console.log('Error: ', error.message) 77 | } 78 | } 79 | 80 | depositEth() 81 | -------------------------------------------------------------------------------- /examples/env.example: -------------------------------------------------------------------------------- 1 | ETH_NODE=http://localhost:8545 2 | WATCHER_URL=http://localhost:7534 3 | WATCHER_PROXY_URL= 4 | PLASMAFRAMEWORK_CONTRACT_ADDRESS=0xc673e4ffcb8464faff908a6804fe0e635af0ea2f 5 | ERC20_CONTRACT_ADDRESS=0xedd7db3a2d8a93edc7f737048f9d84e6c9e40a41 6 | ALICE_ETH_ADDRESS= 7 | ALICE_ETH_ADDRESS_PRIVATE_KEY= 8 | BOB_ETH_ADDRESS= 9 | BOB_ETH_ADDRESS_PRIVATE_KEY= 10 | MILLIS_TO_WAIT_FOR_NEXT_BLOCK=1000 11 | BLOCKS_TO_WAIT_FOR_TXN=20 12 | -------------------------------------------------------------------------------- /examples/exit-erc20.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const Web3 = require('web3') 18 | const RootChain = require('../packages/omg-js-rootchain/src/rootchain') 19 | const ChildChain = require('../packages/omg-js-childchain/src/childchain') 20 | const { getErc20Balance, waitForRootchainTransaction } = require('../packages/omg-js-util/src') 21 | 22 | const config = require('./config.js') 23 | const wait = require('./wait.js') 24 | const getFlags = require('./parse-args') 25 | 26 | // setup for only 1 transaction confirmation block for fast confirmations 27 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node), null, { transactionConfirmationBlocks: 1 }) 28 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 29 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 30 | 31 | async function logBalances (address) { 32 | const rootchainBalance = await getErc20Balance({ 33 | web3, 34 | address, 35 | erc20Address: config.erc20_contract_address 36 | }) 37 | const childchainBalanceArray = await childChain.getBalance(address) 38 | const erc20Object = childchainBalanceArray.find(i => i.currency.toLowerCase() === config.erc20_contract_address.toLowerCase()) 39 | const childchainBalance = erc20Object ? erc20Object.amount : 0 40 | 41 | console.log(`Rootchain ERC20 balance: ${rootchainBalance}`) 42 | console.log(`Childchain ERC20 balance: ${childchainBalance}`) 43 | } 44 | 45 | async function exitErc20 () { 46 | try { 47 | if (!config.erc20_contract_address) { 48 | console.log('Please define an ERC20 contract in your .env') 49 | return 50 | } 51 | const { owner } = getFlags('owner') 52 | const address = config[`${owner}_eth_address`] 53 | const pk = config[`${owner}_eth_address_private_key`] 54 | 55 | const rootchainBalance = await web3.eth.getBalance(address) 56 | const etherBalance = web3.utils.fromWei(String(rootchainBalance), 'ether') 57 | if (etherBalance < 0.001) { 58 | console.log('Not enough ETH on the rootchain to start an exit') 59 | return 60 | } 61 | await logBalances(address) 62 | console.log('-----') 63 | 64 | // get a ERC20 UTXO and exit data 65 | const utxos = await childChain.getUtxos(address) 66 | const utxoToExit = utxos.find(i => i.currency.toLowerCase() === config.erc20_contract_address.toLowerCase()) 67 | if (!utxoToExit) { 68 | console.log('No ERC20 UTXOs to exit') 69 | return 70 | } 71 | 72 | console.log(`Exiting ${utxoToExit.amount} ERC20 with this UTXO:\n${JSON.stringify(utxoToExit, undefined, 2)}`) 73 | 74 | // check if queue exists for this token 75 | const hasToken = await rootChain.hasToken(config.erc20_contract_address) 76 | if (!hasToken) { 77 | console.log(`Adding a ${config.erc20_contract_address} exit queue`) 78 | await rootChain.addToken({ 79 | token: config.erc20_contract_address, 80 | txOptions: { from: address, privateKey: pk } 81 | }) 82 | } 83 | 84 | // start a standard exit 85 | const exitData = await childChain.getExitData(utxoToExit) 86 | const standardExitReceipt = await rootChain.startStandardExit({ 87 | utxoPos: exitData.utxo_pos, 88 | outputTx: exitData.txbytes, 89 | inclusionProof: exitData.proof, 90 | txOptions: { 91 | privateKey: pk, 92 | from: address 93 | } 94 | }) 95 | console.log('Started a standard exit: ', standardExitReceipt.transactionHash) 96 | 97 | const exitId = await rootChain.getStandardExitId({ 98 | txBytes: exitData.txbytes, 99 | utxoPos: exitData.utxo_pos, 100 | isDeposit: utxoToExit.blknum % 1000 !== 0 101 | }) 102 | console.log('Exit id: ', exitId) 103 | 104 | const { msUntilFinalization } = await rootChain.getExitTime({ 105 | exitRequestBlockNumber: standardExitReceipt.blockNumber, 106 | submissionBlockNumber: utxoToExit.blknum 107 | }) 108 | 109 | await wait.wait(msUntilFinalization) 110 | const processExitReceipt = await rootChain.processExits({ 111 | token: config.erc20_contract_address, 112 | exitId: 0, 113 | maxExitsToProcess: 20, 114 | txOptions: { 115 | privateKey: pk, 116 | from: address 117 | } 118 | }) 119 | if (processExitReceipt) { 120 | console.log(`ERC20 exits processing: ${processExitReceipt.transactionHash}`) 121 | await waitForRootchainTransaction({ 122 | web3, 123 | transactionHash: processExitReceipt.transactionHash, 124 | checkIntervalMs: config.millis_to_wait_for_next_block, 125 | blocksToWait: config.blocks_to_wait_for_txn, 126 | onCountdown: (remaining) => console.log(`${remaining} blocks remaining before confirmation`) 127 | }) 128 | } 129 | console.log('Exits processed') 130 | 131 | console.log('-----') 132 | await logBalances(address) 133 | } catch (error) { 134 | console.log('Error: ', error.message) 135 | } 136 | } 137 | 138 | exitErc20() 139 | -------------------------------------------------------------------------------- /examples/exit-eth.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const Web3 = require('web3') 18 | const RootChain = require('../packages/omg-js-rootchain/src/rootchain') 19 | const ChildChain = require('../packages/omg-js-childchain/src/childchain') 20 | const { transaction, waitForRootchainTransaction } = require('../packages/omg-js-util/src') 21 | 22 | const config = require('./config.js') 23 | const wait = require('./wait.js') 24 | const getFlags = require('./parse-args') 25 | 26 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node), null, { transactionConfirmationBlocks: 1 }) 27 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 28 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 29 | 30 | async function logBalances (address) { 31 | const rootchainBalance = await web3.eth.getBalance(address) 32 | const childchainBalanceArray = await childChain.getBalance(address) 33 | const ethObject = childchainBalanceArray.find(i => i.currency === transaction.ETH_CURRENCY) 34 | const childchainEthBalance = ethObject 35 | ? `${web3.utils.fromWei(String(ethObject.amount))} ETH` 36 | : '0 ETH' 37 | 38 | console.log(`Rootchain balance: ${web3.utils.fromWei(String(rootchainBalance), 'ether')} ETH`) 39 | console.log(`Childchain balance: ${childchainEthBalance}`) 40 | } 41 | 42 | async function exitEth () { 43 | try { 44 | const { owner } = getFlags('owner') 45 | const address = config[`${owner}_eth_address`] 46 | const pk = config[`${owner}_eth_address_private_key`] 47 | 48 | const rootchainBalance = await web3.eth.getBalance(address) 49 | const etherBalance = web3.utils.fromWei(String(rootchainBalance), 'ether') 50 | if (etherBalance < 0.001) { 51 | console.log(`${owner} does not have enough ETH on the rootchain to start an exit`) 52 | return 53 | } 54 | await logBalances(address) 55 | console.log('-----') 56 | 57 | // get ETH UTXO and exit data 58 | const utxos = await childChain.getUtxos(address) 59 | const utxoToExit = utxos.find(i => i.currency === transaction.ETH_CURRENCY) 60 | if (!utxoToExit) { 61 | console.log(`${owner} doesnt have any ETH UTXOs to exit`) 62 | return 63 | } 64 | 65 | console.log(`${owner} wants to exit ${web3.utils.fromWei(String(utxoToExit.amount), 'ether')} ETH with this UTXO:\n${JSON.stringify(utxoToExit, undefined, 2)}`) 66 | 67 | // check if queue exists for this token 68 | const hasToken = await rootChain.hasToken(transaction.ETH_CURRENCY) 69 | if (!hasToken) { 70 | console.log(`Adding a ${transaction.ETH_CURRENCY} exit queue`) 71 | await rootChain.addToken({ 72 | token: transaction.ETH_CURRENCY, 73 | txOptions: { from: address, privateKey: pk } 74 | }) 75 | } 76 | 77 | // start a standard exit 78 | const exitData = await childChain.getExitData(utxoToExit) 79 | 80 | const standardExitReceipt = await rootChain.startStandardExit({ 81 | utxoPos: exitData.utxo_pos, 82 | outputTx: exitData.txbytes, 83 | inclusionProof: exitData.proof, 84 | txOptions: { 85 | privateKey: pk, 86 | from: address 87 | } 88 | }) 89 | console.log('Started a standard exit: ', standardExitReceipt.transactionHash) 90 | 91 | const exitId = await rootChain.getStandardExitId({ 92 | txBytes: exitData.txbytes, 93 | utxoPos: exitData.utxo_pos, 94 | isDeposit: utxoToExit.blknum % 1000 !== 0 95 | }) 96 | console.log('Exit id: ', exitId) 97 | 98 | const { msUntilFinalization } = await rootChain.getExitTime({ 99 | exitRequestBlockNumber: standardExitReceipt.blockNumber, 100 | submissionBlockNumber: utxoToExit.blknum 101 | }) 102 | 103 | await wait.wait(msUntilFinalization) 104 | const processExitReceipt = await rootChain.processExits({ 105 | token: transaction.ETH_CURRENCY, 106 | exitId: 0, 107 | maxExitsToProcess: 20, 108 | txOptions: { 109 | privateKey: pk, 110 | from: address 111 | } 112 | }) 113 | if (processExitReceipt) { 114 | console.log(`ETH exits processing: ${processExitReceipt.transactionHash}`) 115 | await waitForRootchainTransaction({ 116 | web3, 117 | transactionHash: processExitReceipt.transactionHash, 118 | checkIntervalMs: config.millis_to_wait_for_next_block, 119 | blocksToWait: config.blocks_to_wait_for_txn, 120 | onCountdown: (remaining) => console.log(`${remaining} blocks remaining before confirmation`) 121 | }) 122 | } 123 | console.log('Exits processed') 124 | 125 | console.log('-----') 126 | await logBalances(address) 127 | } catch (error) { 128 | console.log('Error: ', error.message) 129 | } 130 | } 131 | 132 | exitEth() 133 | -------------------------------------------------------------------------------- /examples/merge-utxos.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const ChildChain = require('../packages/omg-js-childchain/src/childchain') 18 | const transaction = require('../packages/omg-js-util/src/transaction') 19 | const config = require('./config.js') 20 | const wait = require('./wait.js') 21 | const getFlags = require('./parse-args') 22 | const childChain = new ChildChain({ 23 | watcherUrl: config.watcher_url, 24 | watcherProxyUrl: config.watcher_proxy_url, 25 | plasmaContractAddress: config.plasmaframework_contract_address 26 | }) 27 | 28 | async function mergeUtxos () { 29 | try { 30 | const { owner } = getFlags('owner') 31 | const address = config[`${owner}_eth_address`] 32 | const pk = config[`${owner}_eth_address_private_key`] 33 | 34 | const utxos = await childChain.getUtxos(address) 35 | const utxosToMerge = utxos 36 | .filter((u) => u.currency === transaction.ETH_CURRENCY) 37 | .slice(0, 4) 38 | 39 | if (utxosToMerge.length < 2) { 40 | throw Error('Not enough eth utxos to do a merge') 41 | } 42 | 43 | console.log(`there are ${utxosToMerge.length} eth utxos`) 44 | const mergeResult = await childChain.mergeUtxos({ 45 | utxos: utxosToMerge, 46 | privateKey: pk 47 | }) 48 | 49 | console.log('merge utxo transaction result', JSON.stringify(mergeResult, undefined, 2)) 50 | await wait.waitForUtxo(childChain, address, { ...mergeResult, oindex: 0 }) 51 | const newUtxos = await childChain.getUtxos(address) 52 | const newEthUtxos = newUtxos.filter(u => u.currency === transaction.ETH_CURRENCY) 53 | console.log(`there are now ${newEthUtxos.length} eth utxos`) 54 | } catch (error) { 55 | console.log('Error: ', error.message) 56 | } 57 | } 58 | 59 | mergeUtxos() 60 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "omg-js-examples", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "", 6 | "scripts": { 7 | "audit-check": "audit-ci --moderate" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "bn.js": "^5.0.0", 13 | "human-standard-token-abi": "^2.0.0", 14 | "number-to-bn": "^1.7.0", 15 | "omg-json-bigint": "^1.0.0", 16 | "promise-retry": "^1.1.1", 17 | "web3": "1.2.2" 18 | }, 19 | "devDependencies": { 20 | "audit-ci": "^2.5.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/parse-args.js: -------------------------------------------------------------------------------- 1 | module.exports = function (...keys) { 2 | const argMap = {} 3 | for (const position in process.argv) { 4 | const arg = process.argv[position] 5 | for (const key of keys) { 6 | if (arg.includes(key)) { 7 | const value = arg.split(`--${key}=`)[1] 8 | argMap[key] = value 9 | 10 | if (key === 'owner' && value !== 'alice' && value !== 'bob') { 11 | throw Error('Please specify --owner as either alice or bob') 12 | } 13 | if (key === 'from' && value !== 'alice' && value !== 'bob') { 14 | throw Error('Please specify --from as either alice or bob') 15 | } 16 | if (key === 'to' && value !== 'alice' && value !== 'bob') { 17 | throw Error('Please specify --to as either alice or bob') 18 | } 19 | 20 | break 21 | } 22 | } 23 | } 24 | 25 | const missingArgs = [] 26 | for (const key of keys) { 27 | if (!argMap[key]) { 28 | missingArgs.push(`Missing --${key} flag.`) 29 | } 30 | } 31 | if (missingArgs.length) { 32 | throw Error(JSON.stringify(missingArgs, null, 2)) 33 | } 34 | 35 | return argMap 36 | } 37 | -------------------------------------------------------------------------------- /examples/print-balance.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | const Web3 = require('web3') 17 | const config = require('./config.js') 18 | const ChildChain = require('../packages/omg-js-childchain/src/childchain') 19 | const { transaction, getErc20Balance } = require('../packages/omg-js-util/src') 20 | const getFlags = require('./parse-args') 21 | 22 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node), null, { transactionConfirmationBlocks: 1 }) 23 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 24 | 25 | async function printBalance () { 26 | try { 27 | const { owner } = getFlags('owner') 28 | const address = config[`${owner}_eth_address`] 29 | 30 | const balanceArray = await childChain.getBalance(address) 31 | const childchainBalance = balanceArray.map(i => { 32 | return { 33 | currency: i.currency === transaction.ETH_CURRENCY ? 'ETH' : i.currency, 34 | amount: i.currency === transaction.ETH_CURRENCY ? `${web3.utils.fromWei(String(i.amount))} ETH` : i.amount 35 | } 36 | }) 37 | const rootchainBalance = await web3.eth.getBalance(address) 38 | const rootchainBalances = [ 39 | { 40 | currency: 'ETH', 41 | amount: `${web3.utils.fromWei(String(rootchainBalance), 'ether')} ETH` 42 | } 43 | ] 44 | 45 | if (config.erc20_contract_address) { 46 | const aliceRootchainERC20Balance = await getErc20Balance({ 47 | web3, 48 | address: address, 49 | erc20Address: config.erc20_contract_address 50 | }) 51 | rootchainBalances.push({ 52 | currency: config.erc20_contract_address, 53 | amount: aliceRootchainERC20Balance 54 | }) 55 | } 56 | 57 | console.log(`Rootchain balance: ${JSON.stringify(rootchainBalances, null, 2)}`) 58 | console.log(`Childchain balance: ${JSON.stringify(childchainBalance, null, 2)}`) 59 | } catch (error) { 60 | console.log('Error: ', error.message) 61 | } 62 | } 63 | 64 | printBalance() 65 | -------------------------------------------------------------------------------- /examples/print-utxos.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const ChildChain = require('../packages/omg-js-childchain/src/childchain') 18 | const config = require('./config.js') 19 | const getFlags = require('./parse-args') 20 | const JSONBigNumber = require('omg-json-bigint') 21 | 22 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 23 | 24 | async function printUtxos () { 25 | try { 26 | const { owner } = getFlags('owner') 27 | const address = config[`${owner}_eth_address`] 28 | 29 | const utxos = await childChain.getUtxos(address) 30 | console.log(JSONBigNumber.stringify(utxos, undefined, 2)) 31 | } catch (error) { 32 | console.log('Error: ', error.message) 33 | } 34 | } 35 | 36 | printUtxos() 37 | -------------------------------------------------------------------------------- /examples/process-exits.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | const Web3 = require('web3') 15 | const RootChain = require('../packages/omg-js-rootchain/src/rootchain') 16 | const { transaction, waitForRootchainTransaction } = require('../packages/omg-js-util/src') 17 | const getFlags = require('./parse-args') 18 | const config = require('./config.js') 19 | 20 | // setup for fast confirmations 21 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node), null, { 22 | transactionConfirmationBlocks: 1 23 | }) 24 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 25 | 26 | async function processExits () { 27 | try { 28 | const { owner } = getFlags('owner') 29 | const address = config[`${owner}_eth_address`] 30 | const pk = config[`${owner}_eth_address_private_key`] 31 | 32 | const rootchainBalance = await web3.eth.getBalance(address) 33 | console.log(`Rootchain balance: ${web3.utils.fromWei(String(rootchainBalance), 'ether')} ETH`) 34 | console.log('-----') 35 | 36 | const ethQueue = await rootChain.getExitQueue() 37 | if (ethQueue.length) { 38 | console.log('Current ETH exit queue: ', ethQueue) 39 | const ethExitReceipt = await rootChain.processExits({ 40 | token: transaction.ETH_CURRENCY, 41 | exitId: 0, 42 | maxExitsToProcess: ethQueue.length, 43 | txOptions: { 44 | privateKey: pk, 45 | from: address 46 | } 47 | }) 48 | if (ethExitReceipt) { 49 | console.log(`ETH exits processing: ${ethExitReceipt.transactionHash}`) 50 | await waitForRootchainTransaction({ 51 | web3, 52 | transactionHash: ethExitReceipt.transactionHash, 53 | checkIntervalMs: config.millis_to_wait_for_next_block, 54 | blocksToWait: config.blocks_to_wait_for_txn, 55 | onCountdown: (remaining) => console.log(`${remaining} blocks remaining before confirmation`) 56 | }) 57 | console.log('ETH exits processed') 58 | } 59 | } else { 60 | console.log('No exits in ETH exit queue to process') 61 | } 62 | 63 | if (!config.erc20_contract_address) { 64 | console.log('No ERC20 contract defined in config') 65 | return 66 | } 67 | 68 | const hasToken = await rootChain.hasToken(config.erc20_contract_address) 69 | if (!hasToken) { 70 | console.log('No exit queue exists for the ERC20 token') 71 | return 72 | } 73 | const erc20Queue = await rootChain.getExitQueue(config.erc20_contract_address) 74 | if (erc20Queue.length) { 75 | console.log('Current ERC20 exit queue: ', erc20Queue) 76 | const erc20ExitReceipt = await rootChain.processExits({ 77 | token: config.erc20_contract_address, 78 | exitId: 0, 79 | maxExitsToProcess: erc20Queue.length, 80 | txOptions: { 81 | privateKey: pk, 82 | from: address 83 | } 84 | }) 85 | if (erc20ExitReceipt) { 86 | console.log(`ERC20 exits processing: ${erc20ExitReceipt.transactionHash}`) 87 | await waitForRootchainTransaction({ 88 | web3, 89 | transactionHash: erc20ExitReceipt.transactionHash, 90 | checkIntervalMs: config.millis_to_wait_for_next_block, 91 | blocksToWait: config.blocks_to_wait_for_txn, 92 | onCountdown: (remaining) => console.log(`${remaining} blocks remaining before confirmation`) 93 | }) 94 | console.log('ERC20 exits processed') 95 | } 96 | } else { 97 | console.log('No exits in ERC20 exit queue to process') 98 | } 99 | } catch (error) { 100 | console.log('Error: ', error.message) 101 | } 102 | } 103 | 104 | processExits() 105 | -------------------------------------------------------------------------------- /examples/transaction-erc20.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const ChildChain = require('../packages/omg-js-childchain/src/childchain') 18 | const { transaction, waitForChildchainBalance } = require('../packages/omg-js-util/src') 19 | const getFlags = require('./parse-args') 20 | const config = require('./config.js') 21 | 22 | const rootChainPlasmaContractAddress = config.plasmaframework_contract_address 23 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 24 | 25 | async function logBalances (address) { 26 | const balanceArray = await childChain.getBalance(address) 27 | const erc20Object = balanceArray.find(i => i.currency.toLowerCase() === config.erc20_contract_address.toLowerCase()) 28 | const childchainErc20Balance = erc20Object ? erc20Object.amount : 0 29 | 30 | console.log(`Childchain ERC20 balance: ${childchainErc20Balance}`) 31 | return { erc20Balance: childchainErc20Balance } 32 | } 33 | 34 | async function transactionErc20 () { 35 | try { 36 | if (!config.erc20_contract_address) { 37 | console.log('Please define an ERC20 contract address in your .env') 38 | return 39 | } 40 | 41 | const { from, to, amount } = getFlags('from', 'to', 'amount') 42 | const fromAddress = config[`${from}_eth_address`] 43 | const fromPk = config[`${from}_eth_address_private_key`] 44 | const toAddress = config[`${to}_eth_address`] 45 | 46 | const { erc20Balance } = await logBalances(toAddress) 47 | console.log('-----') 48 | 49 | const payments = [{ 50 | owner: toAddress, 51 | currency: config.erc20_contract_address, 52 | amount: Number(amount) 53 | }] 54 | const fee = { currency: transaction.ETH_CURRENCY } 55 | 56 | const createdTxn = await childChain.createTransaction({ 57 | owner: fromAddress, 58 | payments, 59 | fee, 60 | metadata: 'hello world' 61 | }) 62 | console.log(`Created a childchain transaction of ${amount} ERC20`) 63 | // type/sign/build/submit 64 | const typedData = transaction.getTypedData(createdTxn.transactions[0], rootChainPlasmaContractAddress) 65 | const privateKeys = new Array(createdTxn.transactions[0].inputs.length).fill(fromPk) 66 | const signatures = childChain.signTransaction(typedData, privateKeys) 67 | const signedTxn = childChain.buildSignedTransaction(typedData, signatures) 68 | const receipt = await childChain.submitTransaction(signedTxn) 69 | console.log('Transaction submitted: ', receipt.txhash) 70 | 71 | console.log('Waiting for transaction to be recorded by the watcher...') 72 | const expectedAmount = Number(amount) + erc20Balance 73 | await waitForChildchainBalance({ 74 | childChain, 75 | address: toAddress, 76 | expectedAmount, 77 | currency: config.erc20_contract_address 78 | }) 79 | 80 | console.log('-----') 81 | await logBalances(toAddress) 82 | } catch (error) { 83 | console.log('Error: ', error.message) 84 | } 85 | } 86 | 87 | transactionErc20() 88 | -------------------------------------------------------------------------------- /examples/transaction-eth.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const BigNumber = require('bn.js') 18 | const Web3 = require('web3') 19 | const ChildChain = require('../packages/omg-js-childchain/src/childchain') 20 | const { transaction, waitForChildchainBalance } = require('../packages/omg-js-util/src') 21 | const getFlags = require('./parse-args') 22 | const config = require('./config.js') 23 | 24 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node), null, { transactionConfirmationBlocks: 1 }) 25 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 26 | const rootChainPlasmaContractAddress = config.plasmaframework_contract_address 27 | 28 | async function logBalances (address) { 29 | const balanceArray = await childChain.getBalance(address) 30 | const ethObject = balanceArray.find(i => i.currency === transaction.ETH_CURRENCY) 31 | const childchainEthBalance = ethObject 32 | ? `${web3.utils.fromWei(String(ethObject.amount))} ETH` 33 | : '0 ETH' 34 | 35 | console.log(`Recipient childchain ETH balance: ${childchainEthBalance}`) 36 | return { ethBalance: ethObject ? ethObject.amount : 0 } 37 | } 38 | 39 | async function transactionEth () { 40 | try { 41 | const { from, to, amount } = getFlags('from', 'to', 'amount') 42 | const fromAddress = config[`${from}_eth_address`] 43 | const fromPk = config[`${from}_eth_address_private_key`] 44 | const toAddress = config[`${to}_eth_address`] 45 | const transferAmount = new BigNumber(web3.utils.toWei(amount, 'ether')) 46 | 47 | const { ethBalance } = await logBalances(toAddress) 48 | console.log('-----') 49 | 50 | const payments = [{ 51 | owner: toAddress, 52 | currency: transaction.ETH_CURRENCY, 53 | amount: transferAmount 54 | }] 55 | const fee = { currency: transaction.ETH_CURRENCY } 56 | 57 | const createdTxn = await childChain.createTransaction({ 58 | owner: fromAddress, 59 | payments, 60 | fee, 61 | metadata: 'hello' 62 | }) 63 | 64 | console.log(`Created a childchain transaction of ${web3.utils.fromWei(payments[0].amount.toString(), 'ether')} ETH`) 65 | 66 | // type/sign/build/submit 67 | const typedData = transaction.getTypedData(createdTxn.transactions[0], rootChainPlasmaContractAddress) 68 | const privateKeys = new Array(createdTxn.transactions[0].inputs.length).fill(fromPk) 69 | const signatures = childChain.signTransaction(typedData, privateKeys) 70 | const signedTxn = childChain.buildSignedTransaction(typedData, signatures) 71 | const receipt = await childChain.submitTransaction(signedTxn) 72 | console.log('Transaction submitted: ', receipt.txhash) 73 | 74 | // wait for transaction to be recorded by the watcher 75 | console.log('Waiting for transaction to be recorded by the watcher...') 76 | const expectedAmount = transferAmount.add(new BigNumber(ethBalance)) 77 | await waitForChildchainBalance({ 78 | childChain, 79 | address: toAddress, 80 | expectedAmount 81 | }) 82 | 83 | console.log('-----') 84 | await logBalances(toAddress) 85 | } catch (error) { 86 | console.log('Error: ', error.message) 87 | } 88 | } 89 | 90 | transactionEth() 91 | -------------------------------------------------------------------------------- /examples/wait.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const promiseRetry = require('promise-retry') 17 | function wait (ms) { 18 | console.log(`Waiting for ${ms * 0.00001667} min...`) 19 | return new Promise((resolve, reject) => setTimeout(resolve, ms)) 20 | } 21 | 22 | async function waitForChallengePeriodToEnd (rootChain) { 23 | const minExitPeriod = 24 | (await rootChain.plasmaContract.methods.minExitPeriod().call({ from: rootChain.plasmaContractAddress })) * 1000 25 | const waitMs = Number(minExitPeriod) * 2 26 | 27 | await wait(waitMs) 28 | console.log('Challenge period finished') 29 | } 30 | 31 | async function waitForUtxo (childChain, address, utxo) { 32 | return promiseRetry( 33 | async (retry, number) => { 34 | console.log('waiting for utxo...') 35 | const utxos = await childChain.getUtxos(address) 36 | const found = utxos.find( 37 | (u) => 38 | u.oindex === utxo.oindex && 39 | u.txindex === utxo.txindex && 40 | u.blknum === utxo.blknum 41 | ) 42 | if (!found) { 43 | retry() 44 | } 45 | }, 46 | { 47 | minTimeout: 6000, 48 | factor: 1, 49 | retries: 50 50 | } 51 | ) 52 | } 53 | 54 | module.exports = { 55 | waitForChallengePeriodToEnd, 56 | wait, 57 | waitForUtxo 58 | } 59 | -------------------------------------------------------------------------------- /integration-docs/signing-methods.md: -------------------------------------------------------------------------------- 1 | ## Signing a transaction 2 | 3 | The OMG Network childchain uses `eth_signTypedData` as described in [EIP-712](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md) 4 | 5 | This means that a web3 provider (e.g. Metamask) can do the signing. 6 | 7 | ### Creating and signing with `eth_signTypedData` 8 | The steps involved in creating and signing a transaction are as follows: 9 | 1. Create the transaction body by specifying inputs, outputs and optional metadata. 10 | 2. Convert the transaction body into `typedData`format. 11 | 3. Call `eth_signTypedData` to sign it. Note that you must `JSON.stringify()` the typed data. 12 | 4. Assemble the signatures and the transaction body into an RLP encoded format. 13 | 5. Submit the transaction to the child chain. 14 | 15 | ``` 16 | // create the transaction body 17 | const txBody = transaction.createTransactionBody(fromAddress, fromUtxos, toAddress, toAmount, currency) 18 | 19 | // Get the transaction typed data 20 | const typedData = transaction.getTypedData(txBody, verificationContract) 21 | 22 | // Sign the typed data 23 | const signatures = await new Promise((resolve, reject) => { 24 | web3.currentProvider.sendAsync( 25 | { 26 | method: 'eth_signTypedData_v3', 27 | params: [signer, JSON.stringify(typedData)], 28 | from: signer 29 | }, 30 | (err, result) => { 31 | if (err) { 32 | reject(err) 33 | } else if (result.error) { 34 | reject(result.error.message) 35 | } else { 36 | resolve(result.result) 37 | } 38 | } 39 | ) 40 | }) 41 | 42 | // Build the signed transaction 43 | const signedTx = childchain.buildSignedTransaction(typedData, signatures) 44 | 45 | // Submit transaction 46 | const tx = await childchain.submitTransaction(signedTx) 47 | ``` 48 | 49 | ### Creating and signing without `eth_signTypedData` 50 | If you want to manage your private keys yourself, you don't have to use `eth_signTypedData`. You must create a hash of the typed data and sign that using `ecsign`. There is a convience function provided to do this: `childchain.signTransaction(typedData, privateKeys)` . Or you can do it manually, similar to the above: 51 | 52 | 1. Create the transaction body by specifying inputs, outputs and optional metadata. 53 | 2. Convert the transaction body into `typedData`format. 54 | 3. Call `transaction.getToSignHash(typedData)` to get the hash to sign 55 | 4. Sign it using elliptic curve signing (e.g. `ecsign`) 56 | 5. Assemble the signatures and the transaction body into an RLP encoded format. 57 | 6. Submit the transaction to the child chain. 58 | 59 | ``` 60 | // create the transaction body 61 | const txBody = transaction.createTransactionBody(fromAddress, fromUtxos, toAddress, toAmount, currency) 62 | 63 | // Get the transaction typed data 64 | const typedData = transaction.getTypedData(txBody, verificationContract) 65 | 66 | // Get the hash to sign 67 | const toSign = transaction.getToSignHash(typedData) 68 | 69 | // Sign the hashed data (once for each input spent) 70 | const privateKeys = [...the private keys of the spent inputs...] 71 | const signatures = privateKeys.map(key => ecsign(toSign, key)) 72 | 73 | // Build the signed transaction 74 | const signedTx = childchain.buildSignedTransaction(typedData, signatures) 75 | 76 | // Submit transaction 77 | const tx = await childchain.submitTransaction(signedTx) 78 | ``` 79 | 80 | 81 | -------------------------------------------------------------------------------- /integration-docs/transactions.md: -------------------------------------------------------------------------------- 1 | ## Sending a transaction 2 | 3 | The `omg-js` api provides a few different ways to send a transaction on the OMG Network. 4 | 5 | The process of creating and submitting a transaction follows this process: 6 | 1. define a transaction body: definition -> transactionBody 7 | 2. convert that transaction body into typedData: transactionBody -> typedData 8 | 3. sign and encode that typedData into a signed transaction: typedData -> signed transaction 9 | - *or* sign and submit the typedData directly: typedData -> transaction receipt 10 | 4. submit the signed transaction to the childchain: signed transaction -> transaction receipt 11 | 12 | Create a transaction body 13 | - `childchain.createTransaction` (uses the childchain endpoint to construct transaction bodies) 14 | - `transaction.createTransactionBody` (uses local logic to construct a transaction body) 15 | - manually define the transaction body yourself 16 | 17 | ### METHOD A 18 | For full control of the transaction 19 | 1. create a transaction body using a method explained above 20 | 2. `transaction.getTypedData` (sanitizes transaction into the correct typedData format) 21 | 3. `childchain.signTransaction` (locally signs typedData with passed private keys, useful for multiple different signatures. can also use metamask to sign: see [signing-methods](./signing-methods.md)) 22 | 4. `childchain.buildSignedTransaction` (returns encoded and signed transaction ready to be submitted) 23 | 5. `childchain.submitTransaction` (submits to the childchain) 24 | 25 | ### METHOD B 26 | Relying on the childchain completely to create and send the transaction. Note that this method will choose the UTXO for you by using the largest utxo of that specific currency (also you won't able to do 2 transactions in the same block using this method). 27 | 1. `childchain.createTransaction` (will construct a transaction body using the childchain) 28 | 2. `childchain.signTypedData` (locally will sign a transaction selected from `childchain.createTransaction`) 29 | 3. `childchain.submitTyped` (will submit the result of `childchain.signTypedData`) 30 | 31 | ### METHOD C 32 | If you only need to define which UTXOs can be spent 33 | 1. `childchain.getUtxos` (first get all available UTXOs and then select what you want to spend) 34 | 2. `childchain.sendTransaction` (internally will create/getTyped/sign/buildSigned/submit from the utxos you pass it) 35 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "4.1.2-1.0.4", 6 | "command": { 7 | "publish": { 8 | "allowBranch": [ 9 | "master", 10 | "v*" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "omg-js", 3 | "version": "4.1.1-1.0.4", 4 | "private": true, 5 | "description": "JavaScript Library to interact with OMG Network", 6 | "scripts": { 7 | "lerna-reset": "lerna clean --yes && lerna bootstrap", 8 | "release": "npm run build && lerna publish --exact", 9 | "release-fromgit": "lerna publish from-git", 10 | "release-frompackage": "lerna publish from-package", 11 | "postinstall": "npm run lerna-reset", 12 | "lint": "eslint . --ext .js", 13 | "lint-fix": "eslint . --ext .js --fix", 14 | "build": "browserify browserify.js -o packages/browser-omg-js/omg.js", 15 | "build-doc": "documentation build packages/omg-js/src/** packages/omg-js-childchain/src/** packages/omg-js-rootchain/src/** packages/omg-js-util/src/** --config documentation.yml -f html -o docs", 16 | "mocha": "mocha --timeout=0 --bail --slow=0 --check-leaks --full-trace", 17 | "test": "mocha packages/omg-js-childchain/test packages/omg-js-rootchain/test packages/omg-js-util/test", 18 | "sequence-test": "npm run mocha packages/integration-tests/sequence-test", 19 | "ci-min-smoke-test": "npm run mocha packages/integration-tests/ci-min-smoke-test", 20 | "ci-baseline-test": "npm run mocha packages/integration-tests/ci-baseline-test", 21 | "ci-ife-test": "npm run mocha packages/integration-tests/ci-ife-test", 22 | "parallel-test": "node packages/integration-tests/parallel-test", 23 | "cleanup-faucets": "node packages/integration-tests/cleanup-faucets", 24 | "audit-check": "audit-ci --moderate", 25 | "audit-check-omg-js": "cd packages/omg-js && npm run audit-check", 26 | "audit-check-omg-browser": "cd packages/browser-omg-js && npm run audit-check", 27 | "audit-check-omg-childchain": "cd packages/omg-js-childchain && npm run audit-check", 28 | "audit-check-omg-rootchain": "cd packages/omg-js-rootchain && npm run audit-check", 29 | "audit-check-omg-util": "cd packages/omg-js-util && npm run audit-check", 30 | "audit-check-omg-react-native": "cd packages/react-native-omg-js && npm run audit-check", 31 | "audit-all": "npm run audit-check-root && npm run audit-check-omg-js && npm run audit-check-omg-browser && npm run audit-check-omg-childchain && npm run audit-check-omg-rootchain && npm run audit-check-omg-util && npm run audit-check-omg-react-native" 32 | }, 33 | "author": "OmiseGo", 34 | "contributors": [ 35 | "Pong Cheecharern <@Pongch>", 36 | "Kevin Sullivan <@kevsul>", 37 | "Jarindr Thitadilaka <@jarindr>", 38 | "Nicholas Mueller <@nicholasmueller>" 39 | ], 40 | "license": "Apache-2.0", 41 | "repository": { 42 | "type": "git", 43 | "url": "https://github.com/omisego/omg-js.git" 44 | }, 45 | "bugs": { 46 | "url": "https://github.com/omisego/omg-js/issues" 47 | }, 48 | "devDependencies": { 49 | "audit-ci": "^2.5.1", 50 | "chai": "^4.1.2", 51 | "chai-as-promised": "^7.1.1", 52 | "documentation": "^12.1.3", 53 | "dotenv": "^6.0.0", 54 | "eslint": "^6.6.0", 55 | "eslint-config-standard": "^14.1.0", 56 | "eslint-plugin-import": "^2.18.2", 57 | "eslint-plugin-mocha": "^6.2.1", 58 | "eslint-plugin-node": "^10.0.0", 59 | "eslint-plugin-promise": "^4.2.1", 60 | "eslint-plugin-standard": "^4.0.1", 61 | "lerna": "^3.18.4", 62 | "mocha": "^5.2.0", 63 | "mocha-parallel-tests": "^2.2.2", 64 | "sinon": "^7.5.0", 65 | "standard": "^12.0.1", 66 | "truffle": "^4.1.14", 67 | "webpack": "^4.17.1", 68 | "webpack-cli": "^3.1.0" 69 | }, 70 | "standard": { 71 | "env": [ 72 | "mocha" 73 | ], 74 | "ignore": [ 75 | "docs", 76 | "dist", 77 | "packages/integration-tests/tokens" 78 | ] 79 | }, 80 | "dependencies": { 81 | "browserify": "^16.5.1" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /packages/browser-omg-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@omisego/browser-omg-js", 3 | "version": "4.1.2-1.0.4", 4 | "description": "omg-js for the browser", 5 | "keywords": [ 6 | "OMG", 7 | "OmiseGo", 8 | "Plasma" 9 | ], 10 | "main": "omg.js", 11 | "scripts": { 12 | "audit-check": "audit-ci --moderate" 13 | }, 14 | "author": "OmiseGo", 15 | "contributors": [ 16 | "Pong Cheecharern <@Pongch>", 17 | "Kevin Sullivan <@kevsul>", 18 | "Jarindr Thitadilaka <@jarindr>", 19 | "Nicholas Mueller <@nicholasmueller>" 20 | ], 21 | "license": "Apache-2.0", 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/omisego/omg-js.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/omisego/omg-js/issues" 28 | }, 29 | "publishConfig": { 30 | "access": "public" 31 | }, 32 | "devDependencies": { 33 | "audit-ci": "^2.5.1" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/integration-tests/.npmrc: -------------------------------------------------------------------------------- 1 | unsafe-perm = true -------------------------------------------------------------------------------- /packages/integration-tests/README.md: -------------------------------------------------------------------------------- 1 | ## Setup 2 | 3 | To run the integration tests, there is some manual setup involved: 4 | 5 | 1. `geth`, `childchain` and `watcher` need to be running. See [elixir-omg](https://github.com/omisego/elixir-omg) for instructions. 6 | 2. Create and populate `.env` file in the root of `omg-js`. See `env.docker.example` as an example. 7 | 3. Geth `eth.accounts[0]` must have a blank password and hold some test ETH. 8 | 4. The test ERC20 contract must be deployed, and its address added to your environment. You can do this by running `npm run deploy-test-erc20` from the `packages/integration-tests` directory. The ERC20 contract address appears in the resulting output. Add this to your `.env` file. 9 | 5. Finally, run the tests 10 | 11 | ## Running Tests 12 | 13 | There are 2 options to run the test suite 14 | 1. The quickest option is to run them in parallel using `npm run parallel-test`. 15 | 2. The slowest option is to run them all in sequence using `npm run sequence-test` 16 | 17 | ## Returning Test Funds 18 | 19 | Each test file uses its own dedicated faucet (identified by the FAUCET_SALT defined in your test config). If you need to return these funds back to the fund account, you can run `npm run cleanup-faucets`. 20 | 21 | *Childchain funds will be returned to the fundAccount's childchain account. 22 | 23 | Note that some funds may fail to be returned: 24 | 1. Not having enough balance to cover transaction gas costs 25 | 2. Not having an ETH UTXO to cover fees on the childchain 26 | 27 | ## Important 28 | 29 | - If running the full test suite, you must be running against contracts that have a minimum exit period of at least 120 seconds. -------------------------------------------------------------------------------- /packages/integration-tests/ci-baseline-test.js: -------------------------------------------------------------------------------- 1 | require('./test/mergeUtxoTest') 2 | require('./test/metadataTest') 3 | require('./test/amountTypes') 4 | require('./test/decodeTxBytesTest') 5 | require('./test/addTokenTest') 6 | require('./test/depositTest') 7 | require('./test/createTransactionTest') 8 | require('./test/createSubmitTypedTransactionTest') 9 | require('./test/getExitQueueTest') 10 | require('./test/exitWithoutWatcherTest') 11 | -------------------------------------------------------------------------------- /packages/integration-tests/ci-ife-test.js: -------------------------------------------------------------------------------- 1 | require('./test/inFlightExitTest') 2 | require('./test/deleteNonPiggybackedInFlightExitTest') 3 | require('./test/inFlightExitChallengeTest') 4 | require('./test/inFlightExitChallengeResponseTest') 5 | require('./test/challengeInFlightExitInputSpentTest') 6 | require('./test/challengeInFlightExitOutputSpentTest') 7 | -------------------------------------------------------------------------------- /packages/integration-tests/ci-min-smoke-test.js: -------------------------------------------------------------------------------- 1 | require('./test/CORSHeaderTest') 2 | require('./test/depositTest') 3 | require('./test/transfer/simpleEthTransferTest') 4 | require('./test/simpleStartStandardExitTest') 5 | -------------------------------------------------------------------------------- /packages/integration-tests/cleanup-faucets.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | // Use this script to return any funds in faucet accounts back to the fund account 17 | 18 | const RootChain = require('@omisego/omg-js-rootchain') 19 | const ChildChain = require('@omisego/omg-js-childchain') 20 | const Web3 = require('web3') 21 | const fs = require('fs') 22 | 23 | const faucet = require('./helpers/faucet') 24 | const config = require('./test-config') 25 | 26 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node)) 27 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 28 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 29 | 30 | async function cleanup () { 31 | console.log(`🧹 Returning faucet funds back to the fund account: ${config.fund_account}`) 32 | const allFiles = fs.readdirSync(`${__dirname}/test/`) 33 | const fundingPromises = [] 34 | 35 | for (const faucetName of allFiles) { 36 | await faucet.init({ rootChain, childChain, web3, config, faucetName, topup: false }) 37 | fundingPromises.push( 38 | faucet.returnFunds(faucet.faucetAccount, faucet.fundAccount) 39 | ) 40 | } 41 | 42 | await Promise.all(fundingPromises) 43 | console.log(`🧹 All faucet funds returned to fund account: ${config.fund_account}`) 44 | } 45 | 46 | cleanup() 47 | -------------------------------------------------------------------------------- /packages/integration-tests/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "2.3" 2 | services: 3 | postgres: 4 | image: postgres:9.6.13-alpine 5 | restart: always 6 | ports: 7 | - "5432:5432" 8 | environment: 9 | POSTGRES_USER: omisego_dev 10 | POSTGRES_PASSWORD: omisego_dev 11 | POSTGRES_DB: omisego_dev 12 | healthcheck: 13 | test: pg_isready -U omisego_dev 14 | interval: 5s 15 | timeout: 3s 16 | retries: 5 17 | 18 | geth: 19 | image: ethereum/client-go:v1.9.12 20 | entrypoint: 21 | - /bin/sh 22 | - -c 23 | - | 24 | apk add --update curl 25 | # Configures geth with the deployer and authority accounts. This includes: 26 | # 1. Configuring the deployer's keystore 27 | # 2. Configuring the authority's keystore 28 | # 3. Configuring the keystores' password 29 | # 4. Unlocking the accounts by their indexes 30 | # CAREFUL with --allow-insecure-unlock! 31 | echo "" > /tmp/geth-blank-password 32 | # Starts geth 33 | # Websocket is not used by the applications but enabled for debugging/testing convenience 34 | geth --miner.gastarget 7500000 \ 35 | --miner.gasprice "10" \ 36 | --nodiscover \ 37 | --maxpeers 0 \ 38 | --datadir data/ \ 39 | --syncmode 'full' \ 40 | --networkid 1337 \ 41 | --gasprice '1' \ 42 | --keystore=./data/geth/keystore/ \ 43 | --password /tmp/geth-blank-password \ 44 | --unlock "0,1" \ 45 | --rpc --rpcapi personal,web3,eth,net --rpcaddr 0.0.0.0 --rpcvhosts=* --rpcport=8545 \ 46 | --ws --wsaddr 0.0.0.0 --wsorigins='*' \ 47 | --mine \ 48 | --allow-insecure-unlock 49 | ports: 50 | - "8545:8545" 51 | - "8546:8546" 52 | expose: 53 | - "8546" 54 | - "8545" 55 | volumes: 56 | - ./data:/data 57 | healthcheck: 58 | test: curl geth:8545 59 | interval: 5s 60 | timeout: 3s 61 | retries: 5 62 | 63 | childchain: 64 | image: omisego/child_chain:${ELIXIR_OMG_SHA} 65 | command: "full_local" 66 | container_name: childchain 67 | env_file: 68 | - ./localchain_contract_addresses.env 69 | environment: 70 | - ETHEREUM_NETWORK=LOCALCHAIN 71 | - ETHEREUM_RPC_URL=http://geth:8545 72 | - APP_ENV=local_docker_development 73 | - DD_HOSTNAME=datadog 74 | - DD_DISABLED=true 75 | - DB_PATH=/app/.omg/data 76 | - ETHEREUM_EVENTS_CHECK_INTERVAL_MS=800 77 | - ETHEREUM_STALLED_SYNC_THRESHOLD_MS=20000 78 | - FEE_CLAIMER_ADDRESS=0x3b9f4c1dd26e0be593373b1d36cee2008cbeb837 79 | - FEE_ADAPTER=file 80 | - FEE_SPECS_FILE_PATH=/dev-artifacts/fee_specs.dev.json 81 | - FEE_BUFFER_DURATION_MS=30000 82 | - LOGGER_BACKEND=console 83 | - RELEASE_COOKIE=development 84 | - NODE_HOST=127.0.0.1 85 | restart: always 86 | ports: 87 | - "9656:9656" 88 | expose: 89 | - "9656" 90 | volumes: 91 | - ./priv/dev-artifacts:/dev-artifacts 92 | healthcheck: 93 | test: curl childchain:9656 94 | interval: 30s 95 | timeout: 1s 96 | retries: 5 97 | start_period: 30s 98 | depends_on: 99 | geth: 100 | condition: service_healthy 101 | 102 | watcher: 103 | image: omisego/watcher:${ELIXIR_OMG_SHA} 104 | command: "full_local" 105 | container_name: watcher 106 | env_file: 107 | - ./localchain_contract_addresses.env 108 | environment: 109 | - ETHEREUM_NETWORK=LOCALCHAIN 110 | - ETHEREUM_RPC_URL=http://geth:8545 111 | - CHILD_CHAIN_URL=http://childchain:9656 112 | - PORT=7434 113 | - APP_ENV=local_docker_development 114 | - DD_HOSTNAME=datadog 115 | - DD_DISABLED=true 116 | - DB_PATH=/app/.omg/data 117 | - ETHEREUM_EVENTS_CHECK_INTERVAL_MS=800 118 | - ETHEREUM_STALLED_SYNC_THRESHOLD_MS=20000 119 | - ETHEREUM_BLOCK_TIME_SECONDS=1 120 | - EXIT_PROCESSOR_SLA_MARGIN=5520 121 | - EXIT_PROCESSOR_SLA_MARGIN_FORCED=TRUE 122 | - LOGGER_BACKEND=console 123 | - RELEASE_COOKIE=development 124 | - NODE_HOST=127.0.0.1 125 | restart: always 126 | ports: 127 | - "7434:7434" 128 | expose: 129 | - "7434" 130 | healthcheck: 131 | test: curl watcher:7434 132 | interval: 30s 133 | timeout: 1s 134 | retries: 5 135 | start_period: 30s 136 | depends_on: 137 | childchain: 138 | condition: service_healthy 139 | 140 | watcher_info: 141 | image: omisego/watcher_info:${ELIXIR_OMG_SHA} 142 | command: "full_local" 143 | container_name: watcher_info 144 | env_file: 145 | - ./localchain_contract_addresses.env 146 | environment: 147 | - ETHEREUM_NETWORK=LOCALCHAIN 148 | - ETHEREUM_RPC_URL=http://geth:8545 149 | - CHILD_CHAIN_URL=http://childchain:9656 150 | - DATABASE_URL=postgresql://omisego_dev:omisego_dev@postgres:5432/omisego_dev 151 | - PORT=7534 152 | - APP_ENV=local_docker_development 153 | - DD_HOSTNAME=datadog 154 | - DD_DISABLED=true 155 | - DB_PATH=/app/.omg/data 156 | - ETHEREUM_EVENTS_CHECK_INTERVAL_MS=800 157 | - ETHEREUM_BLOCK_TIME_SECONDS=1 158 | - EXIT_PROCESSOR_SLA_MARGIN=5520 159 | - EXIT_PROCESSOR_SLA_MARGIN_FORCED=TRUE 160 | - LOGGER_BACKEND=console 161 | - RELEASE_COOKIE=development 162 | - NODE_HOST=127.0.0.1 163 | restart: always 164 | ports: 165 | - "7534:7534" 166 | expose: 167 | - "7534" 168 | healthcheck: 169 | test: curl watcher_info:7534 170 | interval: 30s 171 | timeout: 1s 172 | retries: 5 173 | start_period: 30s 174 | depends_on: 175 | childchain: 176 | condition: service_healthy 177 | postgres: 178 | condition: service_healthy 179 | -------------------------------------------------------------------------------- /packages/integration-tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "integration-tests", 3 | "private": true, 4 | "version": "4.1.2-1.0.4", 5 | "description": "", 6 | "main": "index.js", 7 | "scripts": { 8 | "mocha": "mocha --timeout=0 --bail --slow=0 --check-leaks --full-trace", 9 | "deploy-test-erc20": "cd tokens && truffle deploy", 10 | "audit-check": "audit-ci --moderate", 11 | "preinstall": "npx npm-force-resolutions" 12 | }, 13 | "author": "", 14 | "license": "MIT", 15 | "dependencies": { 16 | "@omisego/omg-js-childchain": "4.1.2-1.0.4", 17 | "@omisego/omg-js-rootchain": "4.1.2-1.0.4", 18 | "@omisego/omg-js-util": "4.1.2-1.0.4", 19 | "human-standard-token-abi": "^2.0.0", 20 | "node-fetch": "^2.6.0", 21 | "npm-force-resolutions": "0.0.3", 22 | "promise-retry": "^1.1.1", 23 | "web3": "1.2.2" 24 | }, 25 | "devDependencies": { 26 | "audit-ci": "^2.5.1", 27 | "mocha": "^6.2.3", 28 | "mocha-parallel-tests": "^2.2.2", 29 | "truffle": "^4.1.14" 30 | }, 31 | "resolutions": { 32 | "elliptic": "6.5.3", 33 | "bl": "4.0.3" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/integration-tests/parallel-test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const RootChain = require('@omisego/omg-js-rootchain') 17 | const ChildChain = require('@omisego/omg-js-childchain') 18 | const Web3 = require('web3') 19 | const fs = require('fs') 20 | const os = require('os') 21 | 22 | const faucet = require('./helpers/faucet') 23 | const config = require('./test-config') 24 | 25 | const MochaParallel = require('mocha-parallel-tests').default 26 | const mochaParallel = new MochaParallel({ 27 | enableTimeouts: false, 28 | slow: 0, 29 | useColors: true, 30 | fullStackTrace: true, 31 | reporter: 'list' 32 | }) 33 | 34 | const allFiles = fs.readdirSync(`${__dirname}/test/`) 35 | // tests that dont work well in parallel environment 36 | const skippedTests = [ 37 | 'getExitQueueTest.js' 38 | ] 39 | const files = allFiles.filter(i => !skippedTests.includes(i)) 40 | 41 | for (const test of files) { 42 | mochaParallel.addFile(`${__dirname}/test/${test}`) 43 | } 44 | 45 | async function setup () { 46 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node)) 47 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 48 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 49 | 50 | const start = new Date() 51 | for (const faucetName of files) { 52 | await faucet.init({ rootChain, childChain, web3, config, faucetName }) 53 | console.log(`💰 Test faucet funded for ${faucetName}`) 54 | console.log('\n') 55 | } 56 | const end = new Date() 57 | console.log(`⏳ Total funding time: ${(end - start) / 60000} min`) 58 | } 59 | 60 | async function runner () { 61 | await setup() 62 | 63 | const cores = os.cpus().length 64 | console.log(`🚀 Running ${files.length} test files in parallel`) 65 | console.log(`💻 ${cores} CPI cores available, will run ${cores} tests at a time`) 66 | mochaParallel.run(fails => { 67 | if (fails > 0) { 68 | throw Error(`${fails} failures in test run`) 69 | } 70 | }) 71 | } 72 | 73 | runner() 74 | -------------------------------------------------------------------------------- /packages/integration-tests/sequence-test.js: -------------------------------------------------------------------------------- 1 | // add new test files to the list below 2 | // mocha will run these in order 3 | 4 | require('./test/CORSHeaderTest') 5 | require('./test/metadataTest') 6 | require('./test/amountTypes') 7 | require('./test/decodeTxBytesTest') 8 | require('./test/addTokenTest') 9 | require('./test/depositTest') 10 | require('./test/createTransactionTest') 11 | require('./test/transferTest') 12 | require('./test/createSubmitTypedTransactionTest') 13 | require('./test/getExitQueueTest') 14 | require('./test/standardExitTest') 15 | require('./test/exitWithoutWatcherTest') 16 | require('./test/challengeExitTest') 17 | require('./test/inFlightExitTest') 18 | require('./test/deleteNonPiggybackedInFlightExitTest') 19 | require('./test/inFlightExitChallengeTest') 20 | require('./test/inFlightExitChallengeResponseTest') 21 | require('./test/challengeInFlightExitInputSpentTest') 22 | require('./test/challengeInFlightExitOutputSpentTest') 23 | require('./test/mergeUtxoTest') 24 | -------------------------------------------------------------------------------- /packages/integration-tests/test-config.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | require('dotenv').config() 17 | 18 | const config = { 19 | eth_node: process.env.ETH_NODE, 20 | watcher_url: process.env.WATCHER_URL, 21 | watcher_security_url: process.env.WATCHER_SECURITY_URL, 22 | watcher_proxy_url: process.env.WATCHER_PROXY_URL, 23 | childchain_url: process.env.CHILDCHAIN_URL, 24 | plasmaframework_contract_address: process.env.PLASMAFRAMEWORK_CONTRACT_ADDRESS, 25 | erc20_contract_address: process.env.ERC20_CONTRACT_ADDRESS, 26 | fund_account: process.env.FUND_ACCOUNT, 27 | fund_account_private_key: process.env.FUND_ACCOUNT_PRIVATEKEY, 28 | min_amount_eth_per_test: process.env.MIN_AMOUNT_ETH_PER_TEST, 29 | min_amount_erc20_per_test: process.env.MIN_AMOUNT_ERC20_PER_TEST, 30 | topup_multipler: process.env.TOPUP_MULTIPLIER, 31 | faucet_salt: process.env.FAUCET_SALT 32 | } 33 | 34 | module.exports = config 35 | -------------------------------------------------------------------------------- /packages/integration-tests/test/CORSHeaderTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const config = require('../test-config') 17 | const fetch = require('node-fetch') 18 | const chai = require('chai') 19 | const assert = chai.assert 20 | 21 | describe('CORSHeaderTest.js', function () { 22 | describe('Watcher CORS Header test', function () { 23 | it('should return CORS headers', async function () { 24 | const url = `${config.watcher_url}/status.get` 25 | const response = await fetch(url, { 26 | method: 'POST', 27 | headers: { 'Content-Type': 'application/json' }, 28 | body: JSON.stringify({ 29 | jsonrpc: '2.0', 30 | id: 0 31 | }) 32 | }) 33 | assert.equal(response.headers.get('access-control-allow-origin'), '*') 34 | assert.isNotNull(response.headers.get('access-control-expose-headers')) 35 | assert.isNotNull(response.headers.get('access-control-allow-credentials')) 36 | }) 37 | }) 38 | 39 | describe('Child chain CORS Header test', function () { 40 | it('should return CORS headers', async function () { 41 | const url = `${config.childchain_url}/block.get` 42 | const response = await fetch(url, { 43 | method: 'POST', 44 | headers: { 'Content-Type': 'application/json' }, 45 | body: JSON.stringify({ 46 | jsonrpc: '2.0', 47 | id: 0, 48 | hash: '0x0017372421f9a92bedb7163310918e623557ab5310befc14e67212b660c33bec' 49 | }) 50 | }) 51 | assert.equal(response.headers.get('access-control-allow-origin'), '*') 52 | assert.isNotNull(response.headers.get('access-control-expose-headers')) 53 | assert.isNotNull(response.headers.get('access-control-allow-credentials')) 54 | }) 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /packages/integration-tests/test/addTokenTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. */ 12 | 13 | const { assert, should, use } = require('chai') 14 | const chaiAsPromised = require('chai-as-promised') 15 | const RootChain = require('@omisego/omg-js-rootchain') 16 | const ChildChain = require('@omisego/omg-js-childchain') 17 | const Web3 = require('web3') 18 | 19 | const faucet = require('../helpers/faucet') 20 | const config = require('../test-config') 21 | const rcHelper = require('../helpers/rootChainHelper') 22 | 23 | should() 24 | use(chaiAsPromised) 25 | 26 | const path = require('path') 27 | const faucetName = path.basename(__filename) 28 | 29 | describe('addTokenTest.js', function () { 30 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node)) 31 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 32 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 33 | 34 | const INTIIAL_ALICE_AMOUNT = web3.utils.toWei('0.1', 'ether') 35 | let aliceAccount 36 | 37 | before(async function () { 38 | await faucet.init({ rootChain, childChain, web3, config, faucetName }) 39 | }) 40 | 41 | beforeEach(async function () { 42 | aliceAccount = rcHelper.createAccount(web3) 43 | await faucet.fundRootchainEth(aliceAccount.address, INTIIAL_ALICE_AMOUNT) 44 | await rcHelper.waitForEthBalanceEq(web3, aliceAccount.address, INTIIAL_ALICE_AMOUNT) 45 | }) 46 | 47 | afterEach(async function () { 48 | try { 49 | await faucet.returnFunds(aliceAccount) 50 | } catch (err) { 51 | console.warn(`Error trying to return funds to the faucet: ${err}`) 52 | } 53 | }) 54 | 55 | it('add token should add token if not added before', async function () { 56 | const fakeErc20 = rcHelper.createAccount(web3) 57 | const hasToken = await rootChain.hasToken(fakeErc20.address) 58 | assert.isFalse(hasToken) 59 | return rootChain.addToken({ 60 | token: fakeErc20.address, 61 | txOptions: { from: aliceAccount.address, privateKey: aliceAccount.privateKey } 62 | }).should.be.fulfilled 63 | }) 64 | 65 | it('add token should not add token if added before', async function () { 66 | const fakeErc20 = rcHelper.createAccount(web3) 67 | await rootChain.addToken({ 68 | token: fakeErc20.address, 69 | txOptions: { from: aliceAccount.address, privateKey: aliceAccount.privateKey } 70 | }).should.be.fulfilled 71 | const hasToken = await rootChain.hasToken(fakeErc20.address) 72 | assert.isTrue(hasToken) 73 | return rootChain.addToken({ 74 | token: fakeErc20.address, 75 | txOptions: { from: aliceAccount.address, privateKey: aliceAccount.privateKey } 76 | }).should.be.rejected 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /packages/integration-tests/test/createSubmitTypedTransactionTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. */ 12 | 13 | const config = require('../test-config') 14 | const rcHelper = require('../helpers/rootChainHelper') 15 | const ccHelper = require('../helpers/childChainHelper') 16 | const faucet = require('../helpers/faucet') 17 | const Web3 = require('web3') 18 | const ChildChain = require('@omisego/omg-js-childchain') 19 | const RootChain = require('@omisego/omg-js-rootchain') 20 | const { transaction } = require('@omisego/omg-js-util') 21 | const numberToBN = require('number-to-bn') 22 | const chai = require('chai') 23 | const assert = chai.assert 24 | 25 | const path = require('path') 26 | const faucetName = path.basename(__filename) 27 | 28 | describe('createSubmitTypedTransactionTest.js', function () { 29 | const web3 = new Web3(config.eth_node) 30 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 31 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 32 | let FEE_ETH_AMOUNT 33 | before(async function () { 34 | await faucet.init({ rootChain, childChain, web3, config, faucetName }) 35 | const fees = (await childChain.getFees())['1'] 36 | const { amount } = fees.find(f => f.currency === transaction.ETH_CURRENCY) 37 | FEE_ETH_AMOUNT = numberToBN(amount) 38 | }) 39 | 40 | describe('create a single currency transaction with submitTyped', function () { 41 | const INTIIAL_ALICE_AMOUNT = web3.utils.toWei('.0001', 'ether') 42 | const TRANSFER_AMOUNT = web3.utils.toWei('.0000001', 'ether') 43 | 44 | let aliceAccount 45 | let bobAccount 46 | 47 | beforeEach(async function () { 48 | aliceAccount = rcHelper.createAccount(web3) 49 | bobAccount = rcHelper.createAccount(web3) 50 | await faucet.fundChildchain(aliceAccount.address, INTIIAL_ALICE_AMOUNT, transaction.ETH_CURRENCY) 51 | await ccHelper.waitForBalanceEq(childChain, aliceAccount.address, INTIIAL_ALICE_AMOUNT) 52 | }) 53 | 54 | afterEach(async function () { 55 | try { 56 | await faucet.returnFunds(aliceAccount) 57 | await faucet.returnFunds(bobAccount) 58 | } catch (err) { 59 | console.warn(`Error trying to return funds to the faucet: ${err}`) 60 | } 61 | }) 62 | 63 | it('should create and submit a single currency transaction using submitTyped', async function () { 64 | const payments = [{ 65 | owner: bobAccount.address, 66 | currency: transaction.ETH_CURRENCY, 67 | amount: Number(TRANSFER_AMOUNT) 68 | }] 69 | 70 | const fee = { 71 | currency: transaction.ETH_CURRENCY 72 | } 73 | 74 | const createdTx = await childChain.createTransaction({ 75 | owner: aliceAccount.address, 76 | payments, 77 | fee 78 | }) 79 | assert.equal(createdTx.result, 'complete') 80 | assert.equal(createdTx.transactions.length, 1) 81 | 82 | const txTypedData = childChain.signTypedData(createdTx.transactions[0], [aliceAccount.privateKey]) 83 | const result = await childChain.submitTyped(txTypedData) 84 | console.log(`Submitted transaction: ${JSON.stringify(result)}`) 85 | 86 | // Bob's balance should be TRANSFER_AMOUNT 87 | const bobBalance = await ccHelper.waitForBalanceEq(childChain, bobAccount.address, TRANSFER_AMOUNT) 88 | assert.equal(bobBalance.length, 1) 89 | assert.equal(bobBalance[0].amount.toString(), TRANSFER_AMOUNT) 90 | 91 | // Alice's balance should be INTIIAL_ALICE_AMOUNT - TRANSFER_AMOUNT - FEE_AMOUNT 92 | const aliceBalance = await childChain.getBalance(aliceAccount.address) 93 | assert.equal(aliceBalance.length, 1) 94 | const expected = numberToBN(INTIIAL_ALICE_AMOUNT).sub(numberToBN(TRANSFER_AMOUNT)).sub(FEE_ETH_AMOUNT) 95 | assert.equal(aliceBalance[0].amount.toString(), expected.toString()) 96 | }) 97 | }) 98 | }) 99 | -------------------------------------------------------------------------------- /packages/integration-tests/test/decodeTxBytesTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const config = require('../test-config') 17 | const rcHelper = require('../helpers/rootChainHelper') 18 | const ccHelper = require('../helpers/childChainHelper') 19 | const faucet = require('../helpers/faucet') 20 | const Web3 = require('web3') 21 | const ChildChain = require('@omisego/omg-js-childchain') 22 | const RootChain = require('@omisego/omg-js-rootchain') 23 | const { transaction } = require('@omisego/omg-js-util') 24 | const chai = require('chai') 25 | const assert = chai.assert 26 | 27 | const path = require('path') 28 | const faucetName = path.basename(__filename) 29 | 30 | describe('decodeTxBytesTest.js', function () { 31 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node)) 32 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 33 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 34 | 35 | before(async function () { 36 | await faucet.init({ rootChain, childChain, web3, config, faucetName }) 37 | }) 38 | 39 | describe('Decode txBytes exit data', function () { 40 | const INTIIAL_ALICE_AMOUNT = web3.utils.toWei('.1', 'ether') 41 | const DEPOSIT_AMOUNT = web3.utils.toWei('.0001', 'ether') 42 | 43 | let aliceAccount 44 | 45 | beforeEach(async function () { 46 | aliceAccount = rcHelper.createAccount(web3) 47 | await faucet.fundRootchainEth(aliceAccount.address, INTIIAL_ALICE_AMOUNT) 48 | await rcHelper.waitForEthBalanceEq(web3, aliceAccount.address, INTIIAL_ALICE_AMOUNT) 49 | }) 50 | 51 | afterEach(async function () { 52 | try { 53 | await faucet.returnFunds(aliceAccount) 54 | } catch (err) { 55 | console.warn(`Error trying to return funds to the faucet: ${err}`) 56 | } 57 | }) 58 | 59 | it('should able to decode back the txBytesfrom exitData', async function () { 60 | // Alice deposits ETH into the Plasma contract 61 | await rootChain.deposit({ 62 | amount: DEPOSIT_AMOUNT, 63 | txOptions: { 64 | from: aliceAccount.address, 65 | privateKey: aliceAccount.privateKey 66 | } 67 | }) 68 | await ccHelper.waitForBalanceEq( 69 | childChain, 70 | aliceAccount.address, 71 | DEPOSIT_AMOUNT 72 | ) 73 | console.log(`Alice deposited ${DEPOSIT_AMOUNT} into RootChain contract`) 74 | // Get Alice's deposit utxo 75 | const aliceUtxos = await childChain.getUtxos(aliceAccount.address) 76 | assert.equal(aliceUtxos.length, 1) 77 | assert.equal(aliceUtxos[0].amount.toString(), DEPOSIT_AMOUNT) 78 | 79 | // Get the exit data 80 | const utxoToExit = aliceUtxos[0] 81 | const exitData = await childChain.getExitData(utxoToExit) 82 | assert.containsAllKeys(exitData, ['txbytes', 'proof', 'utxo_pos']) 83 | assert.deepEqual( 84 | { 85 | txType: 1, 86 | txData: 0, 87 | inputs: [], 88 | outputs: [ 89 | { 90 | amount: DEPOSIT_AMOUNT, 91 | outputGuard: aliceAccount.address, 92 | outputType: 1, 93 | currency: '0x0000000000000000000000000000000000000000' 94 | } 95 | ], 96 | metadata: 97 | '0x0000000000000000000000000000000000000000000000000000000000000000' 98 | }, 99 | transaction.decodeTxBytes(exitData.txbytes) 100 | ) 101 | }) 102 | }) 103 | }) 104 | -------------------------------------------------------------------------------- /packages/integration-tests/test/exitWithoutWatcherTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const config = require('../test-config') 17 | const rcHelper = require('../helpers/rootChainHelper') 18 | const ccHelper = require('../helpers/childChainHelper') 19 | const faucet = require('../helpers/faucet') 20 | const Web3 = require('web3') 21 | const ChildChain = require('@omisego/omg-js-childchain') 22 | const RootChain = require('@omisego/omg-js-rootchain') 23 | const chai = require('chai') 24 | const assert = chai.assert 25 | 26 | const path = require('path') 27 | const faucetName = path.basename(__filename) 28 | 29 | describe('exitWithoutWatcherTest.js', function () { 30 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node)) 31 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 32 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 33 | 34 | before(async function () { 35 | await faucet.init({ rootChain, childChain, web3, config, faucetName }) 36 | }) 37 | 38 | describe('exiting a deposit without a watcher', function () { 39 | const INTIIAL_ALICE_AMOUNT = web3.utils.toWei('.1', 'ether') 40 | const TEST_AMOUNT = web3.utils.toWei('.0001', 'ether') 41 | 42 | let aliceAccount 43 | 44 | beforeEach(async function () { 45 | aliceAccount = rcHelper.createAccount(web3) 46 | await faucet.fundRootchainEth(aliceAccount.address, INTIIAL_ALICE_AMOUNT) 47 | await rcHelper.waitForEthBalanceEq(web3, aliceAccount.address, INTIIAL_ALICE_AMOUNT) 48 | }) 49 | 50 | afterEach(async function () { 51 | try { 52 | await faucet.returnFunds(aliceAccount) 53 | } catch (err) { 54 | console.warn(`Error trying to return funds to the faucet: ${err}`) 55 | } 56 | }) 57 | 58 | it('getDepositExitData creates required exit data', async function () { 59 | const depositTransaction = await rootChain.deposit({ 60 | amount: TEST_AMOUNT, 61 | txOptions: { 62 | from: aliceAccount.address, 63 | privateKey: aliceAccount.privateKey 64 | } 65 | }) 66 | 67 | // wait for deposit to be recognized 68 | await ccHelper.waitNumUtxos(childChain, aliceAccount.address, 1) 69 | const aliceUtxos = await childChain.getUtxos(aliceAccount.address) 70 | assert.lengthOf(aliceUtxos, 1) 71 | console.log(`Alice deposited ${TEST_AMOUNT} wei into the RootChain contract`) 72 | 73 | // call the function we are testing 74 | const exitData = await rootChain.getDepositExitData({ 75 | transactionHash: depositTransaction.transactionHash 76 | }) 77 | 78 | // compare it to what the childchain returns 79 | const ccExitData = await childChain.getExitData(aliceUtxos[0]) 80 | 81 | assert.deepEqual(ccExitData, exitData) 82 | console.log('Exit data matches what the childchain would return') 83 | }) 84 | }) 85 | }) 86 | -------------------------------------------------------------------------------- /packages/integration-tests/test/mergeUtxoTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const config = require('../test-config') 17 | const rcHelper = require('../helpers/rootChainHelper') 18 | const ccHelper = require('../helpers/childChainHelper') 19 | const faucet = require('../helpers/faucet') 20 | const Web3 = require('web3') 21 | const ChildChain = require('@omisego/omg-js-childchain') 22 | const RootChain = require('@omisego/omg-js-rootchain') 23 | const chai = require('chai') 24 | const assert = chai.assert 25 | 26 | const path = require('path') 27 | const faucetName = path.basename(__filename) 28 | 29 | describe('mergeUtxoTest.js', function () { 30 | const web3 = new Web3(new Web3.providers.HttpProvider(config.eth_node)) 31 | const childChain = new ChildChain({ 32 | watcherUrl: config.watcher_url, 33 | watcherProxyUrl: config.watcher_proxy_url, 34 | plasmaContractAddress: config.plasmaframework_contract_address 35 | }) 36 | const rootChain = new RootChain({ 37 | web3, 38 | plasmaContractAddress: config.plasmaframework_contract_address 39 | }) 40 | 41 | before(async function () { 42 | await faucet.init({ rootChain, childChain, web3, config, faucetName }) 43 | }) 44 | 45 | describe('merge utxos', function () { 46 | const INTIIAL_ALICE_AMOUNT = web3.utils.toWei('.1', 'ether') 47 | const TEST_AMOUNT = web3.utils.toWei('.0001', 'ether') 48 | 49 | let aliceAccount 50 | 51 | beforeEach(async function () { 52 | aliceAccount = rcHelper.createAccount(web3) 53 | await faucet.fundRootchainEth(aliceAccount.address, INTIIAL_ALICE_AMOUNT) 54 | await rcHelper.waitForEthBalanceEq( 55 | web3, 56 | aliceAccount.address, 57 | INTIIAL_ALICE_AMOUNT 58 | ) 59 | }) 60 | 61 | afterEach(async function () { 62 | try { 63 | await faucet.returnFunds(aliceAccount) 64 | } catch (err) { 65 | console.warn(`Error trying to return funds to the faucet: ${err}`) 66 | } 67 | }) 68 | 69 | it('should merge utxos to a single utxo', async function () { 70 | // The new account should have no initial balance 71 | const initialBalance = await childChain.getBalance(aliceAccount.address) 72 | assert.equal(initialBalance.length, 0) 73 | 74 | // Deposit ETH into the Plasma contract 75 | await rootChain.deposit({ 76 | amount: TEST_AMOUNT, 77 | txOptions: { 78 | from: aliceAccount.address, 79 | privateKey: aliceAccount.privateKey 80 | } 81 | }) 82 | 83 | // Wait for transaction to be mined and reflected in the account's balance 84 | await ccHelper.waitForBalanceEq( 85 | childChain, 86 | aliceAccount.address, 87 | TEST_AMOUNT 88 | ) 89 | 90 | await rootChain.deposit({ 91 | amount: TEST_AMOUNT, 92 | txOptions: { 93 | from: aliceAccount.address, 94 | privateKey: aliceAccount.privateKey 95 | } 96 | }) 97 | 98 | await ccHelper.waitForBalanceEq( 99 | childChain, 100 | aliceAccount.address, 101 | TEST_AMOUNT * 2 102 | ) 103 | 104 | // THe account should have one utxo on the child chain 105 | const utxos = await childChain.getUtxos(aliceAccount.address) 106 | assert.equal(utxos.length, 2) 107 | await childChain.mergeUtxos({ 108 | utxos, 109 | privateKey: aliceAccount.privateKey 110 | }) 111 | await ccHelper.waitNumUtxos(childChain, aliceAccount.address, 1) 112 | const utxosMerged = await childChain.getUtxos(aliceAccount.address) 113 | assert.equal(utxosMerged.length, 1) 114 | assert.equal(utxosMerged[0].amount.toString(), (TEST_AMOUNT * 2).toString()) 115 | }) 116 | }) 117 | }) 118 | -------------------------------------------------------------------------------- /packages/integration-tests/test/transfer/sendTransactionTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const config = require('../../test-config') 17 | const rcHelper = require('../../helpers/rootChainHelper') 18 | const ccHelper = require('../../helpers/childChainHelper') 19 | const faucet = require('../../helpers/faucet') 20 | const Web3 = require('web3') 21 | const ChildChain = require('@omisego/omg-js-childchain') 22 | const RootChain = require('@omisego/omg-js-rootchain') 23 | const { transaction } = require('@omisego/omg-js-util') 24 | const chai = require('chai') 25 | const assert = chai.assert 26 | 27 | const path = require('path') 28 | const faucetName = path.basename(__filename) 29 | 30 | describe('sendTransactionTest.js', function () { 31 | const web3 = new Web3(config.eth_node) 32 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 33 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 34 | let feeEth 35 | 36 | before(async function () { 37 | await faucet.init({ rootChain, childChain, web3, config, faucetName }) 38 | const fees = (await childChain.getFees())['1'] 39 | const { amount } = fees.find(f => f.currency === transaction.ETH_CURRENCY) 40 | feeEth = amount 41 | }) 42 | 43 | describe('sendTransaction test', function () { 44 | const INTIIAL_ALICE_AMOUNT = web3.utils.toWei('.1', 'ether') 45 | // TRANSFER_AMOUNT is deliberately bigger than Number.MAX_SAFE_INTEGER to cause rounding errors if not properly handled 46 | const TRANSFER_AMOUNT = '20000000000000123' 47 | let aliceAccount 48 | let bobAccount 49 | 50 | beforeEach(async function () { 51 | aliceAccount = rcHelper.createAccount(web3) 52 | bobAccount = rcHelper.createAccount(web3) 53 | await faucet.fundChildchain(aliceAccount.address, INTIIAL_ALICE_AMOUNT, transaction.ETH_CURRENCY) 54 | await ccHelper.waitForBalanceEq(childChain, aliceAccount.address, INTIIAL_ALICE_AMOUNT) 55 | }) 56 | 57 | afterEach(async function () { 58 | try { 59 | await faucet.returnFunds(aliceAccount) 60 | await faucet.returnFunds(bobAccount) 61 | } catch (err) { 62 | console.warn(`Error trying to return funds to the faucet: ${err}`) 63 | } 64 | }) 65 | 66 | it('should transfer funds on the childchain with eth currency', async function () { 67 | // Check utxos on the child chain 68 | const utxos = await childChain.getUtxos(aliceAccount.address) 69 | assert.equal(utxos.length, 1) 70 | assert.equal(utxos[0].amount.toString(), INTIIAL_ALICE_AMOUNT) 71 | assert.equal(utxos[0].currency, transaction.ETH_CURRENCY) 72 | 73 | const result = await childChain.sendTransaction({ 74 | fromAddress: aliceAccount.address, 75 | fromUtxos: utxos, 76 | fromPrivateKeys: [aliceAccount.privateKey], 77 | payments: [{ 78 | owner: bobAccount.address, 79 | currency: transaction.ETH_CURRENCY, 80 | amount: TRANSFER_AMOUNT 81 | }], 82 | fee: { 83 | amount: feeEth, 84 | currency: transaction.ETH_CURRENCY 85 | } 86 | }) 87 | console.log(`Submitted transaction: ${JSON.stringify(result)}`) 88 | 89 | // Bob's balance should be TRANSFER_AMOUNT 90 | const balance = await ccHelper.waitForBalanceEq(childChain, bobAccount.address, TRANSFER_AMOUNT) 91 | assert.equal(balance.length, 1) 92 | assert.equal(balance[0].amount.toString(), TRANSFER_AMOUNT) 93 | }) 94 | }) 95 | }) 96 | -------------------------------------------------------------------------------- /packages/integration-tests/test/transfer/simpleErc20TransferTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const config = require('../../test-config') 17 | const rcHelper = require('../../helpers/rootChainHelper') 18 | const ccHelper = require('../../helpers/childChainHelper') 19 | const faucet = require('../../helpers/faucet') 20 | const Web3 = require('web3') 21 | const ChildChain = require('@omisego/omg-js-childchain') 22 | const RootChain = require('@omisego/omg-js-rootchain') 23 | const { transaction } = require('@omisego/omg-js-util') 24 | const chai = require('chai') 25 | const assert = chai.assert 26 | 27 | const path = require('path') 28 | const faucetName = path.basename(__filename) 29 | 30 | describe('simpleErc20TransferTest.js', function () { 31 | const web3 = new Web3(config.eth_node) 32 | const childChain = new ChildChain({ watcherUrl: config.watcher_url, watcherProxyUrl: config.watcher_proxy_url, plasmaContractAddress: config.plasmaframework_contract_address }) 33 | const rootChain = new RootChain({ web3, plasmaContractAddress: config.plasmaframework_contract_address }) 34 | let feeEth 35 | 36 | before(async function () { 37 | await faucet.init({ rootChain, childChain, web3, config, faucetName }) 38 | const fees = (await childChain.getFees())['1'] 39 | const { amount } = fees.find(f => f.currency === transaction.ETH_CURRENCY) 40 | feeEth = amount 41 | }) 42 | 43 | describe('ERC20 transfer', function () { 44 | const ERC20_CURRENCY = config.erc20_contract_address 45 | const INTIIAL_ALICE_AMOUNT_ETH = web3.utils.toWei('.0001', 'ether') 46 | const INTIIAL_ALICE_AMOUNT = 4 47 | const TRANSFER_AMOUNT = 3 48 | 49 | let aliceAccount 50 | let bobAccount 51 | 52 | beforeEach(async function () { 53 | aliceAccount = rcHelper.createAccount(web3) 54 | bobAccount = rcHelper.createAccount(web3) 55 | 56 | // Give some ETH to Alice on the childchain to pay fees 57 | await faucet.fundChildchain(aliceAccount.address, INTIIAL_ALICE_AMOUNT_ETH, transaction.ETH_CURRENCY) 58 | await ccHelper.waitForBalanceEq(childChain, aliceAccount.address, INTIIAL_ALICE_AMOUNT_ETH) 59 | // Give some ERC20 to Alice on the child chain 60 | await faucet.fundChildchain(aliceAccount.address, INTIIAL_ALICE_AMOUNT, ERC20_CURRENCY) 61 | await ccHelper.waitForBalanceEq(childChain, aliceAccount.address, INTIIAL_ALICE_AMOUNT, ERC20_CURRENCY) 62 | }) 63 | 64 | afterEach(async function () { 65 | try { 66 | await faucet.returnFunds(aliceAccount) 67 | await faucet.returnFunds(bobAccount) 68 | } catch (err) { 69 | console.warn(`Error trying to return funds to the faucet: ${err}`) 70 | } 71 | }) 72 | 73 | it('should transfer ERC20 tokens on the childchain', async function () { 74 | // Check utxos on the child chain 75 | const utxos = await childChain.getUtxos(aliceAccount.address) 76 | assert.equal(utxos.length, 2) 77 | 78 | const erc20Utxo = utxos.find(utxo => utxo.currency === ERC20_CURRENCY) 79 | const ethUtxo = utxos.find(utxo => utxo.currency === transaction.ETH_CURRENCY) 80 | assert.equal(erc20Utxo.amount, INTIIAL_ALICE_AMOUNT) 81 | assert.equal(erc20Utxo.currency, ERC20_CURRENCY) 82 | 83 | const CHANGE_AMOUNT = erc20Utxo.amount - TRANSFER_AMOUNT 84 | const CHANGE_AMOUNT_FEE = ethUtxo.amount - feeEth 85 | const txBody = { 86 | inputs: [ethUtxo, erc20Utxo], 87 | outputs: [{ 88 | outputType: 1, 89 | outputGuard: bobAccount.address, 90 | currency: ERC20_CURRENCY, 91 | amount: Number(TRANSFER_AMOUNT) 92 | }, { 93 | outputType: 1, 94 | outputGuard: aliceAccount.address, 95 | currency: ERC20_CURRENCY, 96 | amount: CHANGE_AMOUNT 97 | }, { 98 | outputType: 1, 99 | outputGuard: aliceAccount.address, 100 | currency: transaction.ETH_CURRENCY, 101 | amount: CHANGE_AMOUNT_FEE 102 | }] 103 | } 104 | 105 | // Get the transaction data 106 | const typedData = transaction.getTypedData(txBody, rootChain.plasmaContractAddress) 107 | // Sign it 108 | const signatures = childChain.signTransaction(typedData, [aliceAccount.privateKey, aliceAccount.privateKey]) 109 | // Build the signed transaction 110 | const signedTx = childChain.buildSignedTransaction(typedData, signatures) 111 | // Submit the signed transaction to the childchain 112 | const result = await childChain.submitTransaction(signedTx) 113 | console.log(`Submitted transaction: ${JSON.stringify(result)}`) 114 | 115 | // Bob's balance should be TRANSFER_AMOUNT 116 | let balance = await ccHelper.waitForBalanceEq(childChain, bobAccount.address, TRANSFER_AMOUNT, ERC20_CURRENCY) 117 | assert.equal(balance.length, 1) 118 | assert.equal(balance[0].amount, TRANSFER_AMOUNT) 119 | 120 | // Alice's balance should be CHANGE_AMOUNT 121 | balance = await childChain.getBalance(aliceAccount.address) 122 | const erc20Balance = balance.filter(b => b.currency === ERC20_CURRENCY) 123 | assert.equal(erc20Balance[0].amount, CHANGE_AMOUNT) 124 | }) 125 | }) 126 | }) 127 | -------------------------------------------------------------------------------- /packages/integration-tests/test/transferTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | describe('transferTest.js', function () { 17 | require('./transfer/simpleEthTransferTest') 18 | require('./transfer/simpleErc20TransferTest') 19 | require('./transfer/sendTransactionTest') 20 | require('./transfer/mixCurrencyTransferTest') 21 | require('./transfer/transferWith4InputsAndOutputsTest') 22 | }) 23 | -------------------------------------------------------------------------------- /packages/integration-tests/tokens/contracts/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.16; 2 | 3 | contract ERC20 { 4 | uint256 constant private MAX_UINT256 = 2**256 - 1; 5 | mapping (address => uint256) public balances; 6 | mapping (address => mapping (address => uint256)) public allowed; 7 | uint256 public totalSupply; 8 | 9 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 10 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 11 | 12 | function ERC20(uint256 _initialAmount) public { 13 | balances[msg.sender] = _initialAmount; 14 | totalSupply = _initialAmount; 15 | } 16 | 17 | function transfer(address _to, uint256 _value) public returns (bool success) { 18 | require(balances[msg.sender] >= _value); 19 | balances[msg.sender] -= _value; 20 | balances[_to] += _value; 21 | emit Transfer(msg.sender, _to, _value); 22 | return true; 23 | } 24 | 25 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { 26 | uint256 allowance = allowed[_from][msg.sender]; 27 | require(balances[_from] >= _value && allowance >= _value); 28 | balances[_to] += _value; 29 | balances[_from] -= _value; 30 | if (allowance < MAX_UINT256) { 31 | allowed[_from][msg.sender] -= _value; 32 | } 33 | emit Transfer(_from, _to, _value); 34 | return true; 35 | } 36 | 37 | function balanceOf(address _owner) public view returns (uint256 balance) { 38 | return balances[_owner]; 39 | } 40 | 41 | function approve(address _spender, uint256 _value) public returns (bool success) { 42 | allowed[msg.sender][_spender] = _value; 43 | emit Approval(msg.sender, _spender, _value); 44 | return true; 45 | } 46 | 47 | function allowance(address _owner, address _spender) public view returns (uint256 remaining) { 48 | return allowed[_owner][_spender]; 49 | } 50 | } -------------------------------------------------------------------------------- /packages/integration-tests/tokens/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/integration-tests/tokens/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const Migrations = artifacts.require('./Migrations.sol') 3 | 4 | module.exports = function (deployer) { 5 | deployer.deploy(Migrations) 6 | } 7 | -------------------------------------------------------------------------------- /packages/integration-tests/tokens/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-undef */ 2 | const ERC20 = artifacts.require('./ERC20.sol') 3 | 4 | module.exports = function (deployer, network, accounts) { 5 | deployer.deploy(ERC20, 1000000) 6 | } 7 | -------------------------------------------------------------------------------- /packages/integration-tests/tokens/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | development: { 4 | host: '127.0.0.1', 5 | port: 8545, 6 | network_id: '*' // Match any network id 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@omisego/omg-js-childchain", 3 | "version": "4.1.2-1.0.4", 4 | "description": "Module to interact with OMG ChildChain", 5 | "keywords": [ 6 | "OMG", 7 | "OmiseGo", 8 | "Plasma" 9 | ], 10 | "main": "src/childchain.js", 11 | "scripts": { 12 | "audit-check": "audit-ci --moderate" 13 | }, 14 | "author": "OmiseGo", 15 | "contributors": [ 16 | "Pong Cheecharern <@Pongch>", 17 | "Kevin Sullivan <@kevsul>", 18 | "Jarindr Thitadilaka <@jarindr>", 19 | "Nicholas Mueller <@nicholasmueller>" 20 | ], 21 | "license": "Apache-2.0", 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/omisego/omg-js.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/omisego/omg-js/issues" 28 | }, 29 | "dependencies": { 30 | "@hapi/joi": "^16.1.8", 31 | "@omisego/omg-js-util": "4.1.2-1.0.4", 32 | "axios": "^0.21.1", 33 | "bn.js": "^5.0.0", 34 | "debug": "^4.0.1", 35 | "https-proxy-agent": "^5.0.0", 36 | "omg-json-bigint": "^1.0.0", 37 | "rlp": "^2.1.0", 38 | "web3-utils": "^1.2.5" 39 | }, 40 | "publishConfig": { 41 | "access": "public" 42 | }, 43 | "devDependencies": { 44 | "audit-ci": "^2.5.1", 45 | "axios-mock-adapter": "^1.18.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/src/rpc/rpcApi.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const axios = require('axios') 17 | const debug = require('debug')('omg.childchain.rpc') 18 | const JSONBigNumber = require('omg-json-bigint') 19 | const HttpsProxyAgent = require('https-proxy-agent') 20 | class RpcError extends Error { 21 | constructor ({ code, description, messages }) { 22 | super(description || code + (messages ? `, ${messages.code}` : '')) 23 | this.code = code 24 | } 25 | } 26 | // function to override default behavior of axios, so it doesn't use native JSON.parse 27 | function getTransformResponse () { 28 | return [(data) => data] 29 | } 30 | 31 | function getHttpsProxyAgent (proxyUrl) { 32 | return proxyUrl ? new HttpsProxyAgent({ host: proxyUrl, rejectUnauthorized: false }) : undefined 33 | } 34 | 35 | async function get ({ url, proxyUrl }) { 36 | try { 37 | const options = { 38 | method: 'GET', 39 | url: url, 40 | transformResponse: getTransformResponse(), 41 | httpsAgent: getHttpsProxyAgent(proxyUrl) 42 | } 43 | 44 | const res = await axios.request(options) 45 | return parseResponse(res) 46 | } catch (err) { 47 | throw new Error(err) 48 | } 49 | } 50 | 51 | async function post ({ url, body, proxyUrl }) { 52 | body.jsonrpc = body.jsonrpc || '2.0' 53 | body.id = body.id || 0 54 | try { 55 | const options = { 56 | method: 'POST', 57 | url: url, 58 | headers: { 'Content-Type': 'application/json' }, 59 | data: JSONBigNumber.stringify(body), 60 | transformResponse: getTransformResponse(), 61 | httpsAgent: getHttpsProxyAgent(proxyUrl) 62 | } 63 | const res = await axios.request(options) 64 | return parseResponse(res) 65 | } catch (err) { 66 | throw new Error(err) 67 | } 68 | } 69 | 70 | async function parseResponse (res) { 71 | let data 72 | try { 73 | // Need to use a JSON parser capable of handling uint256 74 | data = JSONBigNumber.parse(res.data) 75 | } catch (err) { 76 | throw new Error(`Unable to parse response from server: ${err}`) 77 | } 78 | debug(`rpc response is ${JSON.stringify(data)}`) 79 | 80 | if (data.success) { 81 | return data.data 82 | } 83 | 84 | throw new RpcError(data.data) 85 | } 86 | 87 | module.exports = { 88 | get, 89 | post, 90 | parseResponse 91 | } 92 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/src/validators/helpers.js: -------------------------------------------------------------------------------- 1 | const Joi = require('@hapi/joi') 2 | const BN = require('bn.js') 3 | const web3Utils = require('web3-utils') 4 | 5 | const validateAddress = Joi.string().custom(value => { 6 | return web3Utils.isAddress(value) 7 | }) 8 | 9 | const validateBn = Joi.any().custom((value, helpers) => { 10 | if (!BN.isBN(value)) { 11 | return helpers.message(`${value} is not an instance of BN.js`) 12 | } 13 | return value 14 | }) 15 | 16 | const validateAmount = Joi.alternatives().try( 17 | Joi.number().integer().required(), 18 | validateBn.required(), 19 | Joi.string().regex(/^\d+$/).required() 20 | ) 21 | 22 | const validatePayment = Joi.object({ 23 | amount: validateAmount.required(), 24 | currency: validateAddress.required(), 25 | owner: validateAddress.required() 26 | }) 27 | 28 | const validatePayments = Joi.array().items(validatePayment) 29 | 30 | const validateMetadata = Joi.string().custom((value, helpers) => { 31 | if (value.startsWith('0x')) { 32 | const hex = Buffer.from(value.replace('0x', ''), 'hex') 33 | if (hex.length !== 32) { 34 | return helpers.message(`"${value}" metadata must be a 32-byte hex string`) 35 | } 36 | return value 37 | } 38 | 39 | const bytesSize = Buffer.from(value).length 40 | if (bytesSize > 32) { 41 | return helpers.message(`"${value}" is too large. metadata cannot be larger than 32 bytes`) 42 | } 43 | return value 44 | }) 45 | 46 | const validateFee = Joi.object({ 47 | amount: validateAmount.required(), 48 | currency: validateAddress.required() 49 | }) 50 | 51 | const validateFeeCurrency = Joi.object({ 52 | currency: validateAddress.required() 53 | }) 54 | 55 | module.exports = { 56 | validateAmount, 57 | validateAddress, 58 | validatePayments, 59 | validatePayment, 60 | validateMetadata, 61 | validateFee, 62 | validateBn, 63 | validateFeeCurrency 64 | } 65 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/src/validators/index.js: -------------------------------------------------------------------------------- 1 | const Joi = require('@hapi/joi') 2 | const { 3 | validateAddress, 4 | validatePayments, 5 | validateMetadata, 6 | validateFee, 7 | validateAmount, 8 | validateFeeCurrency 9 | } = require('./helpers') 10 | 11 | const childchainConstructorSchema = Joi.object({ 12 | watcherUrl: Joi.string().required(), 13 | watcherProxyUrl: Joi.string().allow(''), 14 | watcherSecurityUrl: Joi.string().allow(''), 15 | plasmaContractAddress: validateAddress.required() 16 | }) 17 | 18 | const getUtxosSchema = validateAddress.required() 19 | 20 | const getBalanceSchema = validateAddress.required() 21 | 22 | const getTransactionsSchema = Joi.object({ 23 | address: validateAddress, 24 | metadata: validateMetadata, 25 | blknum: validateAmount, 26 | limit: Joi.number().integer(), 27 | page: Joi.number().integer() 28 | }).unknown() 29 | 30 | const getTransactionSchema = Joi.object({ 31 | id: Joi.string().required() 32 | }) 33 | 34 | const getDepositsSchema = Joi.object({ 35 | address: validateAddress, 36 | limit: Joi.number().integer(), 37 | page: Joi.number().integer() 38 | }) 39 | 40 | const getExitDataSchema = Joi.object({ 41 | blknum: validateAmount, 42 | txindex: Joi.number().integer(), 43 | oindex: Joi.number().integer() 44 | }).unknown() 45 | 46 | const getChallengeDataSchema = Joi.object({ 47 | utxoPos: validateAmount.required() 48 | }) 49 | 50 | const submitTransactionSchema = Joi.object({ 51 | transaction: Joi.string().required() 52 | }) 53 | 54 | const createTransactionSchema = Joi.object({ 55 | owner: validateAddress.required(), 56 | payments: validatePayments.required(), 57 | fee: validateFeeCurrency.required(), 58 | metadata: validateMetadata 59 | }) 60 | 61 | const signTypedDataSchema = Joi.object({ 62 | txData: Joi.object().required(), 63 | privateKeys: Joi.array().items(Joi.string()).required() 64 | }) 65 | 66 | const submitTypedSchema = Joi.object().required() 67 | 68 | const signTransactionSchema = Joi.object({ 69 | typedData: Joi.object().required(), 70 | privateKeys: Joi.array().items(Joi.string()).required() 71 | }) 72 | 73 | const buildSignedTransactionSchema = Joi.object({ 74 | typedData: Joi.object().required(), 75 | signatures: Joi.array().items(Joi.string()).required() 76 | }) 77 | 78 | const sendTransactionSchema = Joi.object({ 79 | fromAddress: validateAddress.required(), 80 | fromUtxos: Joi.array().items(Joi.object()).required(), 81 | fromPrivateKeys: Joi.array().items(Joi.string()).required(), 82 | payments: validatePayments.required(), 83 | fee: validateFee.required(), 84 | metadata: validateMetadata 85 | }) 86 | 87 | const inFlightExitGetOutputChallengeDataSchema = Joi.object({ 88 | txbytes: Joi.string().required(), 89 | outputIndex: Joi.number().integer().required() 90 | }) 91 | 92 | const inFlightExitGetInputChallengeDataSchema = Joi.object({ 93 | txbytes: Joi.string().required(), 94 | inputIndex: Joi.number().integer().required() 95 | }) 96 | 97 | const mergeUtxosSchema = Joi.object({ 98 | utxos: Joi.array().max(4).min(2).items(Joi.object()).required(), 99 | privateKey: Joi.string().required(), 100 | metadata: validateMetadata 101 | }) 102 | 103 | module.exports = { 104 | childchainConstructorSchema, 105 | getUtxosSchema, 106 | getBalanceSchema, 107 | getTransactionsSchema, 108 | getTransactionSchema, 109 | getDepositsSchema, 110 | getExitDataSchema, 111 | getChallengeDataSchema, 112 | createTransactionSchema, 113 | signTypedDataSchema, 114 | submitTypedSchema, 115 | signTransactionSchema, 116 | buildSignedTransactionSchema, 117 | sendTransactionSchema, 118 | inFlightExitGetOutputChallengeDataSchema, 119 | inFlightExitGetInputChallengeDataSchema, 120 | submitTransactionSchema, 121 | mergeUtxosSchema 122 | } 123 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/test/createTransactionTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const ax = require('axios') 17 | const ChildChain = require('../src/childchain') 18 | const sinon = require('sinon') 19 | const { transaction } = require('@omisego/omg-js-util') 20 | 21 | const watcherUrl = 'http://omg-watcher' 22 | const plasmaContractAddress = '0xE009136B58a8B2eEb80cfa18aD2Ea6D389d3A375' 23 | 24 | describe('createTransaction', function () { 25 | before(function () { 26 | sinon.stub(ax, 'request').resolves({ 27 | status: 200, 28 | data: JSON.stringify({ 29 | success: true, 30 | data: 'foobar' 31 | }) 32 | }) 33 | }) 34 | 35 | after(function () { 36 | ax.request.restore() 37 | }) 38 | 39 | it('should convert string amount in payments to BN amount', async function () { 40 | const stringAmount = '1000000' 41 | const params = { 42 | owner: '0xd72afdfa06ae5857a639051444f7608fea1528d4', 43 | payments: [ 44 | { 45 | owner: '0xd72afdfa06ae5857a639051444f7608fea1528d4', 46 | currency: transaction.ETH_CURRENCY, 47 | amount: stringAmount 48 | } 49 | ], 50 | fee: { 51 | currency: '00000000000000000000' 52 | }, 53 | metadata: transaction.NULL_METADATA 54 | } 55 | 56 | const childChain = new ChildChain({ watcherUrl, plasmaContractAddress }) 57 | await childChain.createTransaction(params) 58 | 59 | const expectedCall = { 60 | method: 'POST', 61 | url: 'http://omg-watcher/transaction.create', 62 | headers: { 'Content-Type': 'application/json' }, 63 | data: JSON.stringify({ 64 | owner: '0xd72afdfa06ae5857a639051444f7608fea1528d4', 65 | payments: [ 66 | { 67 | owner: '0xd72afdfa06ae5857a639051444f7608fea1528d4', 68 | currency: transaction.ETH_CURRENCY, 69 | amount: Number(stringAmount) 70 | } 71 | ], 72 | fee: { 73 | currency: '00000000000000000000' 74 | }, 75 | metadata: transaction.NULL_METADATA, 76 | jsonrpc: '2.0', 77 | id: 0 78 | }) 79 | } 80 | 81 | sinon.assert.calledWith(ax.request, sinon.match(expectedCall)) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/test/getBalanceTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const chai = require('chai') 17 | const chaiAsPromised = require('chai-as-promised') 18 | const ChildChain = require('../src/childchain') 19 | const mockAx = require('./mock') 20 | 21 | chai.use(chaiAsPromised) 22 | const assert = chai.assert 23 | 24 | const watcherUrl = 'http://omg-watcher' 25 | const plasmaContractAddress = '0xE009136B58a8B2eEb80cfa18aD2Ea6D389d3A375' 26 | 27 | describe('getBalance', function () { 28 | it('should return the balance of an address', async function () { 29 | const address = '0xd72afdfa06ae5857a639051444f7608fea1528d4' 30 | const expectedObject = [{ 31 | currency: '00000000000000000000', 32 | amount: 1000000000000000000 33 | }] 34 | 35 | mockAx.onPost(`${watcherUrl}/account.get_balance`, { address, jsonrpc: '2.0', id: 0 }).reply(200, JSON.stringify({ success: true, data: expectedObject })) 36 | 37 | const childChain = new ChildChain({ watcherUrl, plasmaContractAddress }) 38 | const result = await childChain.getBalance(address) 39 | assert(Array.isArray(result)) 40 | assert.equal(result.length, 1) 41 | assert.equal(expectedObject[0].currency, result[0].currency) 42 | assert.equal(expectedObject[0].amount.toString(), result[0].amount.toString()) 43 | }) 44 | 45 | it('should throw an error on failure', async function () { 46 | const address = '0x01234' 47 | const errorObject = { 48 | code: 'the_error_code', 49 | description: 'The error description' 50 | } 51 | 52 | mockAx.onPost(`${watcherUrl}/account.get_balance`, { address, jsonrpc: '2.0', id: 0 }).reply(200, JSON.stringify({ success: false, data: errorObject })) 53 | const childChain = new ChildChain({ watcherUrl, plasmaContractAddress }) 54 | return assert.isRejected(childChain.getBalance(address), Error, errorObject.description) 55 | }) 56 | }) 57 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/test/getUtxotest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const mocha = require('mocha') 17 | const describe = mocha.describe 18 | const it = mocha.it 19 | const chai = require('chai') 20 | const chaiAsPromised = require('chai-as-promised') 21 | const ChildChain = require('../src/childchain') 22 | const mockAx = require('./mock') 23 | 24 | chai.use(chaiAsPromised) 25 | const assert = chai.assert 26 | 27 | const watcherUrl = 'http://omg-watcher' 28 | const plasmaContractAddress = '0xE009136B58a8B2eEb80cfa18aD2Ea6D389d3A375' 29 | 30 | describe('getUtxo', () => { 31 | it('should return object with empty array as utxo with an address', async () => { 32 | const address = '0xd72afdfa06ae5857a639051444f7608fea1528d4' 33 | const expectedObject = [] 34 | 35 | mockAx.onPost(`${watcherUrl}/account.get_utxos`, { address, jsonrpc: '2.0', id: 0 }).reply(200, JSON.stringify({ success: true, data: expectedObject })) 36 | 37 | const childChain = new ChildChain({ watcherUrl, plasmaContractAddress }) 38 | const returnUtxo = await childChain.getUtxos(address) 39 | assert.deepEqual(expectedObject, returnUtxo) 40 | }) 41 | 42 | it('should throw an error on failure', async () => { 43 | const address = '0x01234' 44 | const errorObject = { 45 | code: 'the_error_code', 46 | description: 'The error description' 47 | } 48 | 49 | mockAx.onPost(`${watcherUrl}/account.get_utxos`, { address, jsonrpc: '2.0', id: 0 }).reply(200, JSON.stringify({ success: false, data: errorObject })) 50 | 51 | const childChain = new ChildChain({ watcherUrl, plasmaContractAddress }) 52 | return assert.isRejected(childChain.getUtxos(address), Error, errorObject.description) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/test/mock.js: -------------------------------------------------------------------------------- 1 | const MockAdapter = require('axios-mock-adapter') 2 | const axios = require('axios') 3 | const mock = new MockAdapter(axios) 4 | 5 | module.exports = mock 6 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/test/rpcApiTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const { use, assert } = require('chai') 17 | const chaiAsPromised = require('chai-as-promised') 18 | const ax = require('axios') 19 | const sinon = require('sinon') 20 | 21 | const ChildChain = require('../src/childchain') 22 | const rpcApi = require('../src/rpc/rpcApi') 23 | 24 | use(chaiAsPromised) 25 | 26 | const watcherUrl = 'http://omg-watcher' 27 | const proxyUrl = 'http://omg-proxy' 28 | const plasmaContractAddress = '0xE009136B58a8B2eEb80cfa18aD2Ea6D389d3A375' 29 | 30 | describe('rpcApi test', function () { 31 | before(function () { 32 | sinon.stub(ax, 'request').resolves( 33 | { 34 | status: 200, 35 | data: JSON.stringify({ 36 | success: true, 37 | data: 'foobar' 38 | }) 39 | } 40 | ) 41 | }) 42 | after(function () { 43 | ax.request.restore() 44 | }) 45 | it('should call body post as string', async function () { 46 | const res = await rpcApi.post({ 47 | url: watcherUrl, 48 | body: { test: 'object should call string' } 49 | }) 50 | assert.equal(res, 'foobar') 51 | const expectedCall = { 52 | method: 'POST', 53 | url: 'http://omg-watcher', 54 | headers: { 'Content-Type': 'application/json' }, 55 | data: JSON.stringify({ 56 | test: 'object should call string', 57 | jsonrpc: '2.0', 58 | id: 0 59 | }), 60 | transformResponse: sinon.match.array 61 | } 62 | 63 | sinon.assert.calledWith( 64 | ax.request, 65 | sinon.match(expectedCall)) 66 | }) 67 | 68 | it('should get to passed url', async function () { 69 | const res = await rpcApi.get({ url: watcherUrl }) 70 | assert.equal(res, 'foobar') 71 | const expectedCall = { 72 | method: 'GET', 73 | url: 'http://omg-watcher', 74 | transformResponse: sinon.match.array, 75 | httpsAgent: undefined 76 | } 77 | sinon.assert.calledWith(ax.request, expectedCall) 78 | }) 79 | 80 | it('should post to passed url', async function () { 81 | const res = await rpcApi.post({ 82 | url: watcherUrl, 83 | body: { id: 10, foo: 'bar' } 84 | }) 85 | const expectedCall = { 86 | method: 'POST', 87 | url: watcherUrl, 88 | headers: { 'Content-Type': 'application/json' }, 89 | data: JSON.stringify({ id: 10, foo: 'bar', jsonrpc: '2.0' }), 90 | transformResponse: sinon.match.array, 91 | httpsAgent: undefined 92 | } 93 | assert.equal(res, 'foobar') 94 | sinon.assert.calledWith(ax.request, expectedCall) 95 | }) 96 | }) 97 | 98 | describe('rpcApi integration test', function () { 99 | before(function () { 100 | sinon.stub(ax, 'request').resolves( 101 | { 102 | status: 200, 103 | data: JSON.stringify({ 104 | success: true, 105 | data: 'foobar' 106 | }) 107 | } 108 | ) 109 | }) 110 | after(function () { 111 | ax.request.restore() 112 | }) 113 | 114 | it('childchain should pass proxy url if it exists', async function () { 115 | const childchainWithProxy = new ChildChain({ 116 | watcherUrl, 117 | watcherProxyUrl: proxyUrl, 118 | plasmaContractAddress 119 | }) 120 | const res = await childchainWithProxy.getTransaction('0x123') 121 | const expectedCall = { 122 | method: 'POST', 123 | url: `${watcherUrl}/transaction.get`, 124 | headers: { 'Content-Type': 'application/json' }, 125 | data: JSON.stringify({ id: '0x123', jsonrpc: '2.0' }), 126 | transformResponse: sinon.match.array, 127 | httpsAgent: sinon.match.object 128 | } 129 | assert.equal(res, 'foobar') 130 | sinon.assert.calledWith(ax.request, expectedCall) 131 | }) 132 | 133 | it('childchain should not pass proxy url if it doesnt exist', async function () { 134 | const childchainNoProxy = new ChildChain({ watcherUrl, plasmaContractAddress }) 135 | const res = await childchainNoProxy.getTransaction('0xabc') 136 | const expectedCall = { 137 | method: 'POST', 138 | url: `${watcherUrl}/transaction.get`, 139 | headers: { 'Content-Type': 'application/json' }, 140 | data: JSON.stringify({ id: '0xabc', jsonrpc: '2.0' }), 141 | transformResponse: sinon.match.array, 142 | httpsAgent: undefined 143 | } 144 | assert.equal(res, 'foobar') 145 | sinon.assert.calledWith(ax.request, expectedCall) 146 | }) 147 | }) 148 | -------------------------------------------------------------------------------- /packages/omg-js-childchain/test/signTransactionTest.js: -------------------------------------------------------------------------------- 1 | const { sign, transaction } = require('@omisego/omg-js-util') 2 | const assert = require('chai').assert 3 | 4 | describe('signTransaction', function () { 5 | describe('signTransaction', function () { 6 | it('should sign the entire transaction object correctly', function () { 7 | const txBody = { 8 | txType: 1, 9 | inputs: [ 10 | { 11 | amount: 5555431, 12 | blknum: 2000, 13 | currency: '0x0000000000000000000000000000000000000000', 14 | oindex: 1, 15 | owner: '0xf86b5b1c2c8de1ea4dc737c849272340fa3561c5', 16 | txindex: 0, 17 | utxo_pos: 2000000000001 18 | } 19 | ], 20 | outputs: [ 21 | { 22 | outputType: 1, 23 | outputGuard: '0xf86b5b1c2c8de1ea4dc737c849272340fa3561c5', 24 | currency: '0x0000000000000000000000000000000000000000', 25 | amount: 123 26 | }, 27 | { 28 | outputType: 1, 29 | outputGuard: '0xf86b5b1c2c8de1ea4dc737c849272340fa3561c5', 30 | currency: '0x0000000000000000000000000000000000000000', 31 | amount: 5555308 32 | } 33 | ], 34 | txData: 0 35 | } 36 | 37 | const privateKey = 'a07cb7889ab3a164dcc72cb6103f2573c7ef2d4a855810594d2bf25df60bc39e' 38 | const expectedSig = '0x0533fe81a1ad27564475bd43c12c138633b6bf74dcf58ae2efd158ed4d3982483e49c34c82c74612c1d920fc22aff19936f91f1c90941b6f7338d8561420313a1b' 39 | const verifyingContract = '0xecda6da8fddd416837bdcf38d6e17a4a898534eb' 40 | 41 | const typedData = transaction.getTypedData(txBody, verifyingContract) 42 | const toSign = transaction.getToSignHash(typedData) 43 | const signatures = sign(toSign, [privateKey]) 44 | assert.equal(signatures[0], expectedSig) 45 | }) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /packages/omg-js-rootchain/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@omisego/omg-js-rootchain", 3 | "version": "4.1.2-1.0.4", 4 | "description": "Module to interact with the Ethereum RootChain", 5 | "keywords": [ 6 | "OMG", 7 | "OmiseGo", 8 | "Plasma" 9 | ], 10 | "main": "src/rootchain.js", 11 | "scripts": { 12 | "audit-check": "audit-ci --moderate" 13 | }, 14 | "author": "OmiseGo", 15 | "contributors": [ 16 | "Pong Cheecharern <@Pongch>", 17 | "Kevin Sullivan <@kevsul>", 18 | "Jarindr Thitadilaka <@jarindr>", 19 | "Nicholas Mueller <@nicholasmueller>" 20 | ], 21 | "license": "Apache-2.0", 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/omisego/omg-js.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/omisego/omg-js/issues" 28 | }, 29 | "dependencies": { 30 | "@hapi/joi": "^16.1.8", 31 | "@omisego/omg-js-util": "4.1.2-1.0.4", 32 | "abi-decoder": "^2.2.2", 33 | "bn.js": "^5.0.0", 34 | "debug": "^4.0.1", 35 | "ethereumjs-util": "^6.2.0", 36 | "human-standard-token-abi": "^2.0.0", 37 | "web3-utils": "^1.2.3" 38 | }, 39 | "publishConfig": { 40 | "access": "public" 41 | }, 42 | "devDependencies": { 43 | "audit-ci": "^2.5.1" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/omg-js-rootchain/src/contracts/PriorityQueue.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "PriorityQueue", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [], 7 | "name": "heapList", 8 | "outputs": [ 9 | { 10 | "internalType": "uint256[]", 11 | "name": "", 12 | "type": "uint256[]" 13 | } 14 | ], 15 | "payable": false, 16 | "stateMutability": "view", 17 | "type": "function" 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /packages/omg-js-rootchain/src/merkle.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | // derived from: https://github.com/omisego/plasma-contracts/blob/master/plasma_framework/test/helpers/merkle.js 17 | 18 | const web3Utils = require('web3-utils') 19 | const LeafSalt = '0x00' 20 | const NodeSalt = '0x01' 21 | const NullHash = web3Utils.sha3('\0'.repeat(33)) 22 | 23 | class MerkleNode { 24 | constructor (data, left = null, right = null) { 25 | this.data = data 26 | this.left = left 27 | this.right = right 28 | } 29 | } 30 | 31 | class MerkleTree { 32 | constructor (leaves, height = 0) { 33 | const minHeightForLeaves = leaves.length === 1 ? 1 : parseInt(Math.ceil(Math.log2(leaves.length)), 10) 34 | 35 | if (height === 0) { 36 | this.height = minHeightForLeaves 37 | } else if (height > minHeightForLeaves) { 38 | this.height = height 39 | } else { 40 | throw new Error( 41 | `height should be at least ${minHeightForLeaves} for the list of leaves` 42 | ) 43 | } 44 | 45 | this.leafCount = 2 ** this.height 46 | 47 | this.leaves = leaves.map(MerkleTree.hashLeaf) 48 | 49 | const fill = Array.from({ length: this.leafCount - this.leaves.length }, () => NullHash) 50 | this.leaves = this.leaves.concat(fill) 51 | this.tree = [MerkleTree.createNodes(this.leaves)] 52 | this.root = this.createTree(this.tree[0]) 53 | } 54 | 55 | static hashLeaf (leaf) { 56 | return web3Utils.sha3(LeafSalt + leaf.slice(2)) 57 | } 58 | 59 | static createNodes (leaves) { 60 | return leaves.map(leaf => new MerkleNode(leaf)) 61 | } 62 | 63 | createTree (level) { 64 | if (level.length === 1) { 65 | return level[0].data 66 | } 67 | 68 | const levelSize = level.length 69 | const nextLevel = [] 70 | 71 | let i = 0 72 | while (i < levelSize) { 73 | // JS stores hashes as hex-encoded strings 74 | const combinedData = NodeSalt + level[i].data.slice(2) + level[i + 1].data.slice(2) 75 | const combined = web3Utils.sha3(combinedData) 76 | const nextNode = new MerkleNode(combined, level[i], level[i + 1]) 77 | nextLevel.push(nextNode) 78 | i += 2 79 | } 80 | 81 | this.tree.push(nextLevel) 82 | return this.createTree(nextLevel) 83 | } 84 | 85 | getInclusionProof (leaf) { 86 | const hashedLeaf = MerkleTree.hashLeaf(leaf) 87 | 88 | let index = this.leaves.indexOf(hashedLeaf) 89 | if (index === -1) { 90 | throw new Error('Argument is not a leaf in the tree') 91 | } 92 | 93 | let proof = '0x' 94 | for (let i = 0; i < this.height; i++) { 95 | let siblingIndex 96 | if (index % 2 === 0) { 97 | siblingIndex = index + 1 98 | } else { 99 | siblingIndex = index - 1 100 | } 101 | index = Math.floor(index / 2) 102 | 103 | proof += this.tree[i][siblingIndex].data.slice(2) 104 | } 105 | 106 | return proof 107 | } 108 | } 109 | 110 | module.exports = MerkleTree 111 | -------------------------------------------------------------------------------- /packages/omg-js-rootchain/src/txUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Send transaction using web3 3 | * 4 | * @method sendTx 5 | * @private 6 | * @param web3 the web3 object to access the Ethereum network 7 | * @param {object} txDetails transaction details object 8 | * @param {string} privateKey private key 9 | * @param {{ onReceipt: any, onConfirmation: any }} [callbacks] callbacks to use during transaction lifecycle 10 | * @return {Promise<{ transactionHash: string }>} promise that resolves with the transaction hash 11 | */ 12 | async function sendTx ({ web3, txDetails, privateKey, callbacks }) { 13 | const enhancedTxDetails = await setGas(web3, txDetails) 14 | 15 | if (!privateKey) { 16 | // No privateKey, caller will handle signing if necessary 17 | if (web3.version.api && web3.version.api.startsWith('0.2')) { 18 | return new Promise((resolve, reject) => { 19 | web3.eth.sendTransaction(enhancedTxDetails, (err, transactionHash) => { 20 | err 21 | ? reject(err) 22 | : resolve({ transactionHash }) 23 | }) 24 | }) 25 | } 26 | if (callbacks) { 27 | return new Promise((resolve, reject) => { 28 | web3.eth.sendTransaction(enhancedTxDetails) 29 | .on('receipt', callbacks.onReceipt) 30 | .on('confirmation', callbacks.onConfirmation) 31 | .on('transactionHash', hash => resolve({ transactionHash: hash })) 32 | .on('error', reject) 33 | }) 34 | } 35 | return web3.eth.sendTransaction(enhancedTxDetails) 36 | } 37 | 38 | // Sign and send transaction 39 | const signedTx = await web3.eth.accounts.signTransaction(enhancedTxDetails, prefixHex(privateKey)) 40 | if (callbacks) { 41 | return new Promise((resolve, reject) => { 42 | web3.eth.sendSignedTransaction(signedTx.rawTransaction) 43 | .on('receipt', callbacks.onReceipt) 44 | .on('confirmation', callbacks.onConfirmation) 45 | .on('transactionHash', hash => resolve({ transactionHash: hash })) 46 | .on('error', reject) 47 | }) 48 | } 49 | return web3.eth.sendSignedTransaction(signedTx.rawTransaction) 50 | } 51 | 52 | async function setGas (web3, txDetails) { 53 | let enhancedTxDetails = { ...txDetails } 54 | if (!enhancedTxDetails.gasPrice) { 55 | try { 56 | if (web3.version.api && web3.version.api.startsWith('0.2')) { 57 | const gasPrice = await new Promise((resolve, reject) => { 58 | web3.eth.getGasPrice((err, result) => { 59 | if (err) { 60 | reject(err) 61 | } else { 62 | resolve(result) 63 | } 64 | }) 65 | }) 66 | enhancedTxDetails = { ...enhancedTxDetails, gasPrice } 67 | } else { 68 | const gasPrice = await web3.eth.getGasPrice() 69 | enhancedTxDetails = { ...enhancedTxDetails, gasPrice } 70 | } 71 | } catch (err) { 72 | // Default to 1 GWEI 73 | enhancedTxDetails = { ...enhancedTxDetails, gasPrice: '1000000000' } 74 | } 75 | } 76 | 77 | if (!enhancedTxDetails.gas) { 78 | try { 79 | if (web3.version.api && web3.version.api.startsWith('0.2')) { 80 | const gas = await new Promise((resolve, reject) => { 81 | web3.eth.estimateGas(enhancedTxDetails, (err, result) => { 82 | if (err) { 83 | reject(err) 84 | } else { 85 | resolve(result) 86 | } 87 | }) 88 | }) 89 | enhancedTxDetails = { ...enhancedTxDetails, gas } 90 | } else { 91 | const gas = await web3.eth.estimateGas(enhancedTxDetails) 92 | enhancedTxDetails = { ...enhancedTxDetails, gas } 93 | } 94 | } catch (err) { 95 | console.warn(`Error estimating gas: ${err}`) 96 | throw err 97 | } 98 | } 99 | 100 | return enhancedTxDetails 101 | } 102 | 103 | function getTxData (web3, contract, method, ...args) { 104 | if (web3.version.api && web3.version.api.startsWith('0.2')) { 105 | return contract[method].getData(...args) 106 | } else { 107 | return contract.methods[method](...args).encodeABI() 108 | } 109 | } 110 | 111 | function int192toHex (int192) { 112 | if (typeof int192 === 'string') { 113 | return int192.startsWith('0x') ? int192 : `0x${int192}` 114 | } else { 115 | return `0x${int192.toString(16)}` 116 | } 117 | } 118 | 119 | function prefixHex (hexString) { 120 | return hexString.startsWith('0x') ? hexString : `0x${hexString}` 121 | } 122 | 123 | module.exports = { 124 | sendTx, 125 | setGas, 126 | getTxData, 127 | int192toHex 128 | } 129 | -------------------------------------------------------------------------------- /packages/omg-js-rootchain/src/validators/helpers.js: -------------------------------------------------------------------------------- 1 | const Joi = require('@hapi/joi') 2 | const BN = require('bn.js') 3 | const web3Utils = require('web3-utils') 4 | 5 | const validateAddress = Joi.string().custom(value => { 6 | return web3Utils.isAddress(value) 7 | }) 8 | 9 | const validateTxOption = Joi.object({ 10 | gas: Joi.number().integer(), 11 | gasPrice: Joi.string(), 12 | privateKey: Joi.string(), 13 | from: validateAddress.required() 14 | }) 15 | 16 | const validateBn = Joi.any().custom((value, helpers) => { 17 | if (!BN.isBN(value)) { 18 | return helpers.message(`${value} is not an instance of BN.js`) 19 | } 20 | return value 21 | }) 22 | 23 | const validateAmount = Joi.alternatives().try( 24 | Joi.number().integer().required(), 25 | validateBn.required(), 26 | Joi.string().regex(/^\d+$/).required() 27 | ) 28 | 29 | module.exports = { 30 | validateAddress, 31 | validateTxOption, 32 | validateBn, 33 | validateAmount 34 | } 35 | -------------------------------------------------------------------------------- /packages/omg-js-rootchain/test/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/omgnetwork/omg-js/5ae5a322516dd256e57b3d5de787ee0cf7eba9b7/packages/omg-js-rootchain/test/index.js -------------------------------------------------------------------------------- /packages/omg-js-util/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@omisego/omg-js-util", 3 | "version": "4.1.2-1.0.4", 4 | "description": "OMG util module", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "audit-check": "audit-ci --moderate" 8 | }, 9 | "author": "OmiseGo", 10 | "contributors": [ 11 | "Pong Cheecharern <@Pongch>", 12 | "Kevin Sullivan <@kevsul>", 13 | "Jarindr Thitadilaka <@jarindr>", 14 | "Nicholas Mueller <@nicholasmueller>" 15 | ], 16 | "license": "Apache-2.0", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/omisego/omg-js.git" 20 | }, 21 | "bugs": { 22 | "url": "https://github.com/omisego/omg-js/issues" 23 | }, 24 | "publishConfig": { 25 | "access": "public" 26 | }, 27 | "dependencies": { 28 | "@hapi/joi": "^17.1.0", 29 | "bn.js": "^5.1.1", 30 | "eth-sig-util": "^2.1.1", 31 | "ethereumjs-util": "^6.0.0", 32 | "human-standard-token-abi": "^2.0.0", 33 | "js-sha3": "^0.8.0", 34 | "lodash": "^4.17.15", 35 | "number-to-bn": "^1.7.0", 36 | "promise-retry": "^1.1.1", 37 | "rlp": "^2.2.2", 38 | "web3-utils": "^1.2.1" 39 | }, 40 | "devDependencies": { 41 | "audit-ci": "^2.5.1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/address.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const address = { 18 | NULL_ADDRESS: '0x0000000000000000000000000000000000000000', 19 | OMG_ADDRESS: { 20 | MAINNET: '0xd26114cd6ee289accf82350c8d8487fedb8a0c07' 21 | } 22 | } 23 | 24 | module.exports = address 25 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/docTypes.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // A collection of custom types used for documentation with JSDoc 18 | 19 | /** 20 | * A Web3 instance object 21 | * @typedef {Object} Web3 22 | */ 23 | 24 | /** 25 | * An Ethereum transaction receipt 26 | * @typedef {Object} TransactionReceipt 27 | * @property {string} blockHash 28 | * @property {number} blockNumber 29 | * @property {string} contractAddress 30 | * @property {number} cumulativeGasUsed 31 | * @property {string} from 32 | * @property {number} gasUsed 33 | * @property {Object[]} logs 34 | * @property {string} root 35 | * @property {string} to 36 | * @property {string} transactionHash 37 | * @property {number} transactionIndex 38 | */ 39 | 40 | /** 41 | * UTXO 42 | * @typedef {Object} UTXO 43 | * @property {number} amount 44 | * @property {number} blknum 45 | * @property {string} creating_txhash 46 | * @property {string} currency 47 | * @property {number} oindex 48 | * @property {number} otype 49 | * @property {string} owner 50 | * @property {string} spending_txhash 51 | * @property {number} txindex 52 | * @property {number} utxo_pos 53 | */ 54 | 55 | /** 56 | * Decoded output 57 | * @typedef {Object} Output 58 | * @property {number} outputType 59 | * @property {string} outputGuard 60 | * @property {string} currency 61 | * @property {number} amount 62 | */ 63 | 64 | /** 65 | * A transaction object returned from the Watcher 66 | * @typedef {Object} TransactionData 67 | * @property {number} txindex 68 | * @property {string} txhash 69 | * @property {string} metadata 70 | * @property {string} txbytes 71 | * @property {Object} block 72 | * @property {Object[]} inputs 73 | * @property {Object[]} outputs 74 | */ 75 | 76 | /** 77 | * A transaction body object 78 | * @typedef {Object} TransactionBody 79 | * @property {number} txType 80 | * @property {UTXO[]} inputs 81 | * @property {Output[]} outputs 82 | * @property {number} txData 83 | * @property {string} metadata 84 | */ 85 | 86 | /** 87 | * A deposit transaction object 88 | * @typedef {Object} DepositTransaction 89 | * @property {string} owner 90 | * @property {number} amount 91 | * @property {string} currency 92 | */ 93 | 94 | /** 95 | * A BN.js instance 96 | * @typedef {Object} BigNumber 97 | * @property {Function} toString 98 | * @property {Function} toNumber 99 | */ 100 | 101 | /** 102 | * A typed data object 103 | * @typedef {Object} TypedData 104 | * @property {Object} types 105 | * @property {Object} domain 106 | * @property {string} primaryType 107 | * @property {Object} message 108 | */ 109 | 110 | /** 111 | * A transaction options object 112 | * @typedef {Object} TransactionOptions 113 | * @property {string} from 114 | * @property {number} gas 115 | * @property {string} gasPrice 116 | * @property {string} privateKey 117 | */ 118 | 119 | /** 120 | * Transaction life cycle event callbacks 121 | * @typedef {Object} TransactionCallbacks 122 | * @property {Function} onConfirmation 123 | * @property {Function} onReceipt 124 | */ 125 | 126 | /** 127 | * Balance returned from account.get_balance 128 | * @typedef {Object} Balance 129 | * @property {string|number|BigNumber} amount 130 | * @property {string} currency 131 | */ 132 | 133 | /** 134 | * Exit data for UTXO 135 | * @typedef {Object} ExitData 136 | * @property {string} proof 137 | * @property {string} txbytes 138 | * @property {number} utxo_pos 139 | */ 140 | 141 | /** 142 | * Payment object used when sending or creating transactions 143 | * @typedef {Object} Payment 144 | * @property {string} owner 145 | * @property {string} currency 146 | * @property {string|number|BigNumber} amount 147 | */ 148 | 149 | /** 150 | * Fee object used when sending or creating transactions 151 | * @typedef {Object} Fee 152 | * @property {string} currency 153 | */ 154 | 155 | /** 156 | * Fees object information 157 | * @typedef {Object} FeeInfo 158 | * @property {number} amount 159 | * @property {string} currency 160 | * @property {number} pegged_amount 161 | * @property {string} pegged_currency 162 | * @property {number} pegged_subunit_to_unit 163 | * @property {number} subunit_to_unit 164 | * @property {string} updated_at 165 | */ 166 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/ethErrorReason.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const hexPrefix = require('./hexPrefix') 18 | const { ethErrorReasonSchema } = require('./validators') 19 | const Joi = require('@hapi/joi') 20 | 21 | /** 22 | * Retrieve EVM revert reason from the transaction hash 23 | * 24 | * @method ethErrorReason 25 | * @param {Object} args an arguments object 26 | * @param {Web3} args.web3 web3 instance 27 | * @param {string} args.hash transaction hash 28 | * @return {Promise} promise that resolves with the error reason 29 | */ 30 | async function ethErrorReason ({ web3, hash }) { 31 | Joi.assert({ web3, hash }, ethErrorReasonSchema) 32 | const tx = await web3.eth.getTransaction(hash) 33 | if (!tx) { 34 | console.log('Tx not found, cannot get reason') 35 | } else { 36 | const code = await web3.eth.call(tx, tx.blockNumber) 37 | if (code && code.substr(138)) { 38 | const reason = web3.utils.toAscii(hexPrefix(code.substr(138))) 39 | console.log('Revert reason:', reason) 40 | return reason 41 | } else { 42 | console.log('No return value from tx, cannot get reason') 43 | } 44 | } 45 | } 46 | 47 | module.exports = ethErrorReason 48 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/getErc20Balance.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const erc20abi = require('human-standard-token-abi') 18 | const { getErc20BalanceSchema } = require('./validators') 19 | const Joi = require('@hapi/joi') 20 | 21 | /** 22 | * Retrieve the RootChain ERC20 balance for an address 23 | * 24 | * @method getErc20Balance 25 | * @param {Object} args arguments object 26 | * @param {Web3} args.web3 web3 instance 27 | * @param {string} args.address the address to check 28 | * @param {string} args.erc20Address the address of the erc20 contract 29 | * @return {Promise} promise that resolves with the balance 30 | */ 31 | async function getErc20Balance ({ web3, address, erc20Address }) { 32 | Joi.assert({ web3, address, erc20Address }, getErc20BalanceSchema) 33 | const erc20Contract = new web3.eth.Contract(erc20abi, erc20Address) 34 | const txDetails = { 35 | from: address, 36 | to: erc20Address, 37 | data: erc20Contract.methods.balanceOf(address).encodeABI() 38 | } 39 | const balance = await web3.eth.call(txDetails) 40 | // return number as string in case of very large numbers 41 | return web3.utils.hexToNumberString(balance) 42 | } 43 | 44 | module.exports = getErc20Balance 45 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/hexPrefix.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const { hexPrefixSchema } = require('./validators') 18 | const Joi = require('@hapi/joi') 19 | 20 | /** 21 | * Helper to ensure passed value is hex prefixed 22 | * 23 | * @method hexPrefix 24 | * @param {string} data string to prefix 25 | * @return {string} hex prefixed string 26 | */ 27 | function hexPrefix (data) { 28 | Joi.assert({ data }, hexPrefixSchema) 29 | return data.startsWith('0x') ? data : `0x${data}` 30 | } 31 | 32 | module.exports = hexPrefix 33 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/hexToBytes.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const { hexToBytesSchema } = require('./validators') 18 | const Joi = require('@hapi/joi') 19 | 20 | /** 21 | * Helper to transform hex to bytes 22 | * 23 | * @method hexToBytes 24 | * @param {string} hex hex string to transform 25 | * @return {string} transformed value as bytes 26 | */ 27 | function hexToBytes (hex) { 28 | Joi.assert({ hex }, hexToBytesSchema) 29 | hex = hex.toString(16) 30 | hex = hex.replace(/^0x/i, '') 31 | hex = hex.length % 2 ? '0' + hex : hex 32 | 33 | const bytes = [] 34 | for (let c = 0; c < hex.length; c += 2) { 35 | bytes.push(parseInt(hex.substr(c, 2), 16)) 36 | } 37 | 38 | return bytes 39 | }; 40 | 41 | module.exports = hexToBytes 42 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const { getToSignHash, hashTypedDataMessage, hashTypedDataDomain } = require('./signHash') 17 | 18 | module.exports = { 19 | transaction: require('./transaction'), 20 | sign: require('./sign'), 21 | hexToBytes: require('./hexToBytes'), 22 | hexPrefix: require('./hexPrefix'), 23 | ethErrorReason: require('./ethErrorReason'), 24 | getErc20Balance: require('./getErc20Balance'), 25 | waitForRootchainTransaction: require('./waitForRootchainTransaction'), 26 | waitForChildchainBalance: require('./waitForChildchainBalance'), 27 | utxo: require('./utxo'), 28 | getToSignHash, 29 | hashTypedDataMessage, 30 | hashTypedDataDomain 31 | } 32 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/sign.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const { signSchema } = require('./validators') 17 | const Joi = require('@hapi/joi') 18 | global.Buffer = global.Buffer || require('buffer').Buffer 19 | 20 | const ethUtil = require('ethereumjs-util') 21 | const sigUtil = require('eth-sig-util') 22 | 23 | function ecSign (tosign, privateKey) { 24 | const signed = ethUtil.ecsign( 25 | tosign, 26 | Buffer.from(privateKey.replace('0x', ''), 'hex') 27 | ) 28 | return sigUtil.concatSig(signed.v, signed.r, signed.s) 29 | } 30 | 31 | /** 32 | * Sign a transaction with passed private keys 33 | * 34 | * @method sign 35 | * @param {Buffer} tx hash of typedData to be signed 36 | * @param {string[]} privateKeys array of private keys to sign with 37 | * @return {string[]} array of signatures 38 | */ 39 | function sign (tx, privateKeys) { 40 | Joi.assert({ tx, privateKeys }, signSchema) 41 | return privateKeys.map(key => ecSign(tx, key)) 42 | } 43 | 44 | module.exports = sign 45 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/signHash.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const ethUtil = require('ethereumjs-util') 18 | const abi = require('ethereumjs-abi') 19 | 20 | // Recursively finds all the dependencies of a type 21 | function dependencies (types, primaryType, found = []) { 22 | if (found.includes(primaryType)) { 23 | return found 24 | } 25 | if (types[primaryType] === undefined) { 26 | return found 27 | } 28 | found.push(primaryType) 29 | for (const field of types[primaryType]) { 30 | for (const dep of dependencies(types, field.type, found)) { 31 | if (!found.includes(dep)) { 32 | found.push(dep) 33 | } 34 | } 35 | } 36 | return found 37 | } 38 | 39 | function encodeType (types, primaryType) { 40 | // Get dependencies primary first, then alphabetical 41 | let deps = dependencies(types, primaryType) 42 | deps = deps.filter(t => t !== primaryType) 43 | deps = [primaryType].concat(deps.sort()) 44 | 45 | // Format as a string with fields 46 | let result = '' 47 | for (const type of deps) { 48 | result += `${type}(${types[type].map(({ name, type }) => `${type} ${name}`).join(',')})` 49 | } 50 | return result 51 | } 52 | 53 | function typeHash (types, primaryType) { 54 | return ethUtil.keccak256(encodeType(types, primaryType)) 55 | } 56 | 57 | function encodeData (types, primaryType, data) { 58 | const encTypes = [] 59 | const encValues = [] 60 | 61 | // Add typehash 62 | encTypes.push('bytes32') 63 | encValues.push(typeHash(types, primaryType)) 64 | 65 | // Add field contents 66 | for (const field of types[primaryType]) { 67 | let value = data[field.name] 68 | if (field.type === 'string' || field.type === 'bytes') { 69 | encTypes.push('bytes32') 70 | value = ethUtil.keccak256(value) 71 | encValues.push(value) 72 | } else if (types[field.type] !== undefined) { 73 | encTypes.push('bytes32') 74 | value = ethUtil.keccak256(encodeData(types, field.type, value)) 75 | encValues.push(value) 76 | } else if (field.type.lastIndexOf(']') === field.type.length - 1) { 77 | throw new Error('Arrays currently unimplemented in encodeData') 78 | } else { 79 | encTypes.push(field.type) 80 | encValues.push(value) 81 | } 82 | } 83 | 84 | return abi.rawEncode(encTypes, encValues) 85 | } 86 | 87 | function structHash (types, primaryType, data) { 88 | return ethUtil.keccak256(encodeData(types, primaryType, data)) 89 | } 90 | 91 | // We're always going to use the same domain separator, so we can cache 92 | // the domain hash to avoid hashing it every time. 93 | let domainHash 94 | 95 | function getToSignHash (typedData) { 96 | if (!domainHash) { 97 | domainHash = structHash(typedData.types, 'EIP712Domain', typedData.domain) 98 | } 99 | return ethUtil.keccak256( 100 | Buffer.concat([ 101 | Buffer.from('1901', 'hex'), 102 | domainHash, 103 | structHash(typedData.types, typedData.primaryType, typedData.message) 104 | ]) 105 | ) 106 | } 107 | 108 | function hashTypedDataMessage (typedData) { 109 | const messageHash = structHash(typedData.types, typedData.primaryType, typedData.message) 110 | return ethUtil.bufferToHex(messageHash) 111 | } 112 | 113 | function hashTypedDataDomain (typedData) { 114 | const domainHash = structHash(typedData.types, 'EIP712Domain', typedData.domain) 115 | return ethUtil.bufferToHex(domainHash) 116 | } 117 | 118 | module.exports = { 119 | getToSignHash, 120 | hashTypedDataMessage, 121 | hashTypedDataDomain 122 | } 123 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/typedData.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const { NULL_ADDRESS } = require('./address.js') 18 | const NULL_INPUT = { blknum: 0, txindex: 0, oindex: 0 } 19 | const NULL_OUTPUT = { 20 | outputType: 0, 21 | outputGuard: NULL_ADDRESS, 22 | currency: NULL_ADDRESS, 23 | amount: 0 24 | } 25 | const NULL_METADATA = '0x0000000000000000000000000000000000000000000000000000000000000000' 26 | 27 | const domainSpec = [ 28 | { name: 'name', type: 'string' }, 29 | { name: 'version', type: 'string' }, 30 | { name: 'verifyingContract', type: 'address' }, 31 | { name: 'salt', type: 'bytes32' } 32 | ] 33 | 34 | const txSpec = [ 35 | { name: 'txType', type: 'uint256' }, 36 | { name: 'input0', type: 'Input' }, 37 | { name: 'input1', type: 'Input' }, 38 | { name: 'input2', type: 'Input' }, 39 | { name: 'input3', type: 'Input' }, 40 | { name: 'output0', type: 'Output' }, 41 | { name: 'output1', type: 'Output' }, 42 | { name: 'output2', type: 'Output' }, 43 | { name: 'output3', type: 'Output' }, 44 | { name: 'txData', type: 'uint256' }, 45 | { name: 'metadata', type: 'bytes32' } 46 | ] 47 | 48 | const inputSpec = [ 49 | { name: 'blknum', type: 'uint256' }, 50 | { name: 'txindex', type: 'uint256' }, 51 | { name: 'oindex', type: 'uint256' } 52 | ] 53 | 54 | const outputSpec = [ 55 | { name: 'outputType', type: 'uint256' }, 56 | { name: 'outputGuard', type: 'bytes20' }, 57 | { name: 'currency', type: 'address' }, 58 | { name: 'amount', type: 'uint256' } 59 | ] 60 | 61 | const domainData = { 62 | name: 'OMG Network', 63 | version: '1', 64 | verifyingContract: '', 65 | salt: '0xfad5c7f626d80f9256ef01929f3beb96e058b8b4b0e3fe52d84f054c0e2a7a83' 66 | } 67 | 68 | const typedData = { 69 | types: { 70 | EIP712Domain: domainSpec, 71 | Transaction: txSpec, 72 | Input: inputSpec, 73 | Output: outputSpec 74 | }, 75 | domain: domainData, 76 | primaryType: 'Transaction' 77 | } 78 | 79 | function getTypedData (tx, verifyingContract) { 80 | domainData.verifyingContract = verifyingContract 81 | 82 | // Sanitise inputs 83 | const inputs = tx.inputs.map(i => ({ 84 | blknum: i.blknum, 85 | txindex: i.txindex, 86 | oindex: i.oindex 87 | })) 88 | 89 | // Sanitise Outputs 90 | const outputs = tx.outputs.map(o => ({ 91 | outputType: o.outputType || 1, 92 | outputGuard: o.outputGuard || o.owner, 93 | currency: o.currency, 94 | amount: o.amount.toString() 95 | })) 96 | 97 | typedData.message = { 98 | txType: tx.txType || 1, 99 | input0: inputs.length > 0 ? inputs[0] : NULL_INPUT, 100 | input1: inputs.length > 1 ? inputs[1] : NULL_INPUT, 101 | input2: inputs.length > 2 ? inputs[2] : NULL_INPUT, 102 | input3: inputs.length > 3 ? inputs[3] : NULL_INPUT, 103 | output0: outputs.length > 0 ? outputs[0] : NULL_OUTPUT, 104 | output1: outputs.length > 1 ? outputs[1] : NULL_OUTPUT, 105 | output2: outputs.length > 2 ? outputs[2] : NULL_OUTPUT, 106 | output3: outputs.length > 3 ? outputs[3] : NULL_OUTPUT, 107 | txData: tx.txData || 0, 108 | metadata: tx.metadata || NULL_METADATA 109 | } 110 | 111 | return typedData 112 | } 113 | 114 | module.exports = { 115 | getTypedData, 116 | NULL_ADDRESS, 117 | NULL_INPUT, 118 | NULL_OUTPUT, 119 | NULL_METADATA 120 | } 121 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/utxo.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | const numberToBN = require('number-to-bn') 17 | const { uniqBy } = require('lodash') 18 | function mergeUtxosToOutput (utxos) { 19 | if (utxos.length < 2) { 20 | throw new Error('Must merge at least 2 utxos') 21 | } 22 | if (utxos.length > 4) { 23 | throw new Error('Cannot merge more than 4 utxos') 24 | } 25 | const isSameCurrency = uniqBy(utxos, (u) => u.currency).length === 1 26 | if (!isSameCurrency) { 27 | throw new Error('All utxo currency must be the same') 28 | } 29 | 30 | const isSameOwner = uniqBy(utxos, (u) => u.owner).length === 1 31 | if (!isSameOwner) { 32 | throw new Error('All utxo owner must be the same') 33 | } 34 | 35 | return { 36 | outputType: 1, 37 | outputGuard: utxos[0].owner, 38 | currency: utxos[0].currency, 39 | amount: utxos.reduce( 40 | (prev, curr) => prev.add(numberToBN(curr.amount)), 41 | numberToBN(0) 42 | ) 43 | } 44 | } 45 | 46 | module.exports = { 47 | mergeUtxosToOutput 48 | } 49 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/validators/helpers.js: -------------------------------------------------------------------------------- 1 | const Joi = require('@hapi/joi') 2 | const BN = require('bn.js') 3 | const web3Utils = require('web3-utils') 4 | 5 | const validateAddress = Joi.string().custom(value => { 6 | return web3Utils.isAddress(value) 7 | }) 8 | 9 | const validateTxOption = Joi.object({ 10 | gas: Joi.number().integer(), 11 | gasPrice: Joi.string(), 12 | privateKey: Joi.string(), 13 | from: validateAddress 14 | }) 15 | 16 | const validateBn = Joi.any().custom((value, helpers) => { 17 | if (!BN.isBN(value)) { 18 | return helpers.message(`${value} is not an instance of BN.js`) 19 | } 20 | return value 21 | }) 22 | 23 | const validatePayment = Joi.object({ 24 | amount: [Joi.number().unsafe().integer().required(), Joi.string().required(), validateBn.required()], 25 | currency: validateAddress.required(), 26 | owner: validateAddress.required() 27 | }) 28 | 29 | const validatePayments = Joi.array().items(validatePayment) 30 | 31 | const validateMetadata = Joi.string().custom((value, helpers) => { 32 | if (value.startsWith('0x')) { 33 | const hex = Buffer.from(value.replace('0x', ''), 'hex') 34 | if (hex.length !== 32) { 35 | return helpers.message(`"${value}" metadata must be a 32-byte hex string`) 36 | } 37 | return value 38 | } 39 | 40 | const bytesSize = Buffer.from(value).length 41 | if (bytesSize > 32) { 42 | return helpers.message(`"${value}" is too large. metadata cannot be larger than 32 bytes`) 43 | } 44 | return value 45 | }) 46 | 47 | const validateFee = Joi.object({ 48 | amount: [Joi.number().integer().required(), Joi.string().required(), validateBn.required()], 49 | currency: validateAddress.required() 50 | }) 51 | 52 | const validateUtxos = Joi.array().items(Joi.object()) 53 | 54 | const validateAmount = Joi.alternatives().try( 55 | Joi.number().integer().required(), 56 | validateBn.required(), 57 | Joi.string().regex(/^\d+$/).required() 58 | ) 59 | 60 | module.exports = { 61 | validateAddress, 62 | validateTxOption, 63 | validateBn, 64 | validatePayment, 65 | validatePayments, 66 | validateMetadata, 67 | validateFee, 68 | validateUtxos, 69 | validateAmount 70 | } 71 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/validators/index.js: -------------------------------------------------------------------------------- 1 | const Joi = require('@hapi/joi') 2 | const { 3 | validateAddress, 4 | validatePayments, 5 | validateFee, 6 | validateMetadata, 7 | validateUtxos, 8 | validateBn, 9 | validateAmount 10 | } = require('./helpers') 11 | 12 | const encodeDepositSchema = Joi.object({ 13 | owner: validateAddress.required(), 14 | amount: [Joi.number().integer().required(), Joi.string().required(), validateBn.required()], 15 | currency: validateAddress.required() 16 | }) 17 | 18 | const decodeDepositSchema = Joi.object({ 19 | encodedDeposit: Joi.string().required() 20 | }) 21 | 22 | const decodeTxBytesSchema = Joi.object({ 23 | tx: Joi.any().required() 24 | }) 25 | 26 | const createTransactionBodySchema = Joi.object({ 27 | fromAddress: validateAddress.required(), 28 | fromUtxos: validateUtxos.required(), 29 | payments: validatePayments.required(), 30 | fee: validateFee.required(), 31 | metadata: validateMetadata 32 | }) 33 | 34 | const decodeUtxoPosSchema = Joi.object({ 35 | utxoPos: [Joi.number().integer().required(), Joi.string().required(), validateBn.required()] 36 | }) 37 | 38 | const encodeUtxoPosSchema = Joi.object({ 39 | utxo: Joi.object({ 40 | blknum: validateAmount, 41 | txindex: Joi.number().integer(), 42 | oindex: Joi.number().integer() 43 | }).unknown() 44 | }) 45 | 46 | const decodeMetadataSchema = Joi.object({ 47 | str: validateMetadata.required() 48 | }) 49 | 50 | const encodeMetadataSchema = Joi.object({ 51 | str: validateMetadata.required() 52 | }) 53 | 54 | const getTypedDataSchema = Joi.object({ 55 | tx: Joi.object().required(), 56 | verifyingContract: validateAddress.required() 57 | }) 58 | 59 | const getToSignHashSchema = Joi.object({ 60 | typedData: Joi.object().required() 61 | }) 62 | 63 | const getErc20BalanceSchema = Joi.object({ 64 | web3: Joi.any().required(), 65 | address: validateAddress.required(), 66 | erc20Address: validateAddress.required() 67 | }) 68 | 69 | const ethErrorReasonSchema = Joi.object({ 70 | web3: Joi.any().required(), 71 | hash: Joi.string().required() 72 | }) 73 | 74 | const waitForChildchainBalanceSchema = Joi.object({ 75 | childChain: Joi.object().required(), 76 | address: validateAddress.required(), 77 | expectedAmount: [Joi.number().integer().required(), Joi.string().required(), validateBn.required()], 78 | currency: validateAddress.required() 79 | }) 80 | 81 | const waitForRootchainTransactionSchema = Joi.object({ 82 | web3: Joi.any().required(), 83 | transactionHash: Joi.string().required(), 84 | checkIntervalMs: Joi.number().integer().required(), 85 | blocksToWait: Joi.number().integer().required(), 86 | onCountdown: Joi.func() 87 | }) 88 | 89 | const signSchema = Joi.object({ 90 | tx: Joi.any().required(), 91 | privateKeys: Joi.array().items(Joi.string()).required() 92 | }) 93 | 94 | const hexToBytesSchema = Joi.object({ 95 | hex: Joi.string().required() 96 | }) 97 | 98 | const hexPrefixSchema = Joi.object({ 99 | data: Joi.string().required() 100 | }) 101 | 102 | module.exports = { 103 | encodeDepositSchema, 104 | decodeDepositSchema, 105 | encodeUtxoPosSchema, 106 | decodeUtxoPosSchema, 107 | decodeTxBytesSchema, 108 | createTransactionBodySchema, 109 | decodeMetadataSchema, 110 | encodeMetadataSchema, 111 | getTypedDataSchema, 112 | getToSignHashSchema, 113 | getErc20BalanceSchema, 114 | ethErrorReasonSchema, 115 | waitForChildchainBalanceSchema, 116 | waitForRootchainTransactionSchema, 117 | signSchema, 118 | hexToBytesSchema, 119 | hexPrefixSchema 120 | } 121 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/waitForChildchainBalance.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const promiseRetry = require('promise-retry') 18 | const numberToBN = require('number-to-bn') 19 | const transaction = require('./transaction') 20 | const { waitForChildchainBalanceSchema } = require('./validators') 21 | const Joi = require('@hapi/joi') 22 | 23 | /** 24 | * Helper that waits for a balance on the ChildChain to equal the expected amount 25 | * 26 | * @method waitForChildchainBalance 27 | * @param {Object} args arguments object 28 | * @param {ChildChain} args.childChain childchain instance 29 | * @param {string} args.address address to check the balance of 30 | * @param {number} args.expectedAmount the amount expected 31 | * @param {string} [args.currency] the token address to check (defaults to ETH) 32 | * @return {Promise} promise that resolves when the balance equals the expected amount 33 | */ 34 | function waitForChildchainBalance ({ 35 | childChain, 36 | address, 37 | expectedAmount, 38 | currency = transaction.ETH_CURRENCY 39 | }) { 40 | Joi.assert({ childChain, address, expectedAmount, currency }, waitForChildchainBalanceSchema) 41 | return promiseRetry(async (retry, number) => { 42 | const resp = await childChain.getBalance(address) 43 | if (resp.length === 0) retry() 44 | 45 | const currencyExists = resp.find(item => item.currency.toLowerCase() === currency.toLowerCase()) 46 | if (!currencyExists) retry() 47 | 48 | const expectedBn = numberToBN(expectedAmount) 49 | const isEqual = numberToBN(currencyExists.amount).eq(expectedBn) 50 | if (!isEqual) retry() 51 | 52 | return resp 53 | }, { 54 | minTimeout: 6000, 55 | factor: 1, 56 | retries: 50 57 | }) 58 | } 59 | 60 | module.exports = waitForChildchainBalance 61 | -------------------------------------------------------------------------------- /packages/omg-js-util/src/waitForRootchainTransaction.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | const { waitForRootchainTransactionSchema } = require('./validators') 18 | const Joi = require('@hapi/joi') 19 | 20 | /** 21 | * Helper that waits for RootChain confirmation of a transaction 22 | * 23 | * @method waitForRootchainTransaction 24 | * @param {Object} args arguments object 25 | * @param {Web3} args.web3 web3 instance 26 | * @param {string} args.transactionHash hash of the transaction to wait for 27 | * @param {number} args.checkIntervalMs interval in milliseconds to check the current block number 28 | * @param {number} args.blocksToWait number of blocks to wait before transaction is confirmed 29 | * @param {Function} [args.onCountdown] callback thats passed the remaining number of blocks before confirmation 30 | * @return {Promise} promise that resolves with the transaction receipt of the transaction 31 | */ 32 | async function waitForRootchainTransaction ({ 33 | web3, 34 | transactionHash, 35 | checkIntervalMs, 36 | blocksToWait, 37 | onCountdown 38 | }) { 39 | Joi.assert({ web3, transactionHash, checkIntervalMs, blocksToWait, onCountdown }, waitForRootchainTransactionSchema) 40 | let remaining 41 | const transactionReceiptAsync = async (transactionHash, resolve, reject) => { 42 | try { 43 | const transactionReceipt = await web3.eth.getTransactionReceipt(transactionHash) 44 | if (blocksToWait > 0) { 45 | try { 46 | const block = await web3.eth.getBlock(transactionReceipt.blockNumber) 47 | const current = await web3.eth.getBlock('latest') 48 | const remainingCandidate = blocksToWait - (current.number - block.number) 49 | if (remainingCandidate !== remaining) { 50 | remaining = remainingCandidate 51 | if (onCountdown) { 52 | onCountdown(remaining) 53 | } 54 | } 55 | if (current.number - block.number >= blocksToWait) { 56 | const transaction = await web3.eth.getTransaction(transactionHash) 57 | if (transaction.blockNumber !== null) { 58 | return resolve(transactionReceipt) 59 | } else { 60 | return reject(new Error('Transaction with hash: ' + transactionHash + ' ended up in an uncle block.')) 61 | } 62 | } else { 63 | setTimeout(() => transactionReceiptAsync(transactionHash, resolve, reject), checkIntervalMs) 64 | } 65 | } catch (e) { 66 | setTimeout(() => transactionReceiptAsync(transactionHash, resolve, reject), checkIntervalMs) 67 | } 68 | } else { 69 | return resolve(transactionReceipt) 70 | } 71 | } catch (e) { 72 | return reject(e) 73 | } 74 | } 75 | 76 | return new Promise((resolve, reject) => transactionReceiptAsync(transactionHash, resolve, reject)) 77 | } 78 | 79 | module.exports = waitForRootchainTransaction 80 | -------------------------------------------------------------------------------- /packages/omg-js-util/test/encodeMetadataTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const chai = require('chai') 17 | const assert = chai.assert 18 | const transaction = require('../src/transaction') 19 | 20 | describe('Encode metadata tests', function () { 21 | it('should encode/decode a string 1', function () { 22 | const str = '00100' 23 | return assert.equal(transaction.decodeMetadata(transaction.encodeMetadata(str)), str) 24 | }) 25 | it('should encode/decode a string 2', function () { 26 | const str = 'The quick brown fox' 27 | return assert.equal(transaction.decodeMetadata(transaction.encodeMetadata(str)), str) 28 | }) 29 | it('should encode/decode a string 3', function () { 30 | const str = 'Unicode ₿itcoin, さとし' 31 | return assert.equal(transaction.decodeMetadata(transaction.encodeMetadata(str)), str) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /packages/omg-js-util/test/encodeUtxoTest.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | const assert = chai.assert 3 | const transaction = require('../src/transaction') 4 | 5 | describe('Encode UTXO tests', function () { 6 | it('should return the encoded utxo position 1', function () { 7 | const utxo = { 8 | blknum: 96035000, 9 | currency: transaction.ETH_CURRENCY, 10 | oindex: 0, 11 | txbytes: '', 12 | txindex: 0 13 | } 14 | 15 | const utxoPos = transaction.encodeUtxoPos(utxo) 16 | assert.equal(utxoPos.toString(), '96035000000000000') 17 | }) 18 | 19 | it('should return the encoded utxo position 2', function () { 20 | const utxo = { 21 | blknum: 96035000, 22 | currency: transaction.ETH_CURRENCY, 23 | oindex: 1, 24 | txbytes: '', 25 | txindex: 7 26 | } 27 | 28 | const utxoPos = transaction.encodeUtxoPos(utxo) 29 | assert.equal(utxoPos.toString(), '96035000000070001') 30 | }) 31 | 32 | it('should return the encoded utxo position 3', function () { 33 | const utxo = { 34 | blknum: 777777777, 35 | currency: transaction.ETH_CURRENCY, 36 | oindex: 999, 37 | txbytes: '', 38 | txindex: 8888 39 | } 40 | 41 | const utxoPos = transaction.encodeUtxoPos(utxo) 42 | assert.equal(utxoPos.toString(), '777777777088880999') 43 | }) 44 | }) 45 | -------------------------------------------------------------------------------- /packages/omg-js-util/test/signHashTest.js: -------------------------------------------------------------------------------- 1 | const { transaction, hashTypedDataMessage, hashTypedDataDomain } = require('../src') 2 | const assert = require('chai').assert 3 | 4 | const verifyingContract = '0xecda6da8fddd416837bdcf38d6e17a4a898534eb' 5 | const txBody = { 6 | txType: 1, 7 | inputs: [ 8 | { 9 | amount: 5555431, 10 | blknum: 2000, 11 | currency: '0x0000000000000000000000000000000000000000', 12 | oindex: 1, 13 | owner: '0xf86b5b1c2c8de1ea4dc737c849272340fa3561c5', 14 | txindex: 0, 15 | utxo_pos: 2000000000001 16 | } 17 | ], 18 | outputs: [ 19 | { 20 | outputType: 1, 21 | outputGuard: '0xf86b5b1c2c8de1ea4dc737c849272340fa3561c5', 22 | currency: '0x0000000000000000000000000000000000000000', 23 | amount: 123 24 | }, 25 | { 26 | outputType: 1, 27 | outputGuard: '0xf86b5b1c2c8de1ea4dc737c849272340fa3561c5', 28 | currency: '0x0000000000000000000000000000000000000000', 29 | amount: 5555308 30 | } 31 | ], 32 | txData: 0 33 | } 34 | 35 | const typedData = transaction.getTypedData(txBody, verifyingContract) 36 | 37 | describe('hashTypedDataMessage', function () { 38 | it('should hash typed data message correctly', function () { 39 | assert.equal(hashTypedDataMessage(typedData), '0xbbfceed8e6b34851dfacdeef46dce4ec9e14fec48c3ad5a15d5b3efc2d6f2b0e') 40 | }) 41 | }) 42 | 43 | describe('hashTypedDataDomain', function () { 44 | it('should get domain seperator hash correctly', function () { 45 | assert.equal(hashTypedDataDomain(typedData), '0x6c650db4cf8537eca556551a536134f12df128b0628180c031ec65cdb08e6295') 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /packages/omg-js-util/test/validateTransactionTest.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | // testing ETH function 17 | const chai = require('chai') 18 | const assert = chai.assert 19 | const transaction = require('../src/transaction') 20 | 21 | describe('Validate Transaction tests', function () { 22 | it('should fail to create a transaction with non array inputs', function () { 23 | const txBody = { 24 | inputs: { 25 | txindex: 0, 26 | oindex: 0, 27 | currency: '0000000000000000000000000000000000000000', 28 | blknum: 19774001 29 | }, 30 | outputs: [] 31 | } 32 | return assert.throws(() => transaction.validate(txBody), Error, /Inputs must be an array/) 33 | }) 34 | 35 | it('should fail to create a transaction with 0 inputs', function () { 36 | const txBody = { 37 | inputs: [], 38 | outputs: [] 39 | } 40 | return assert.throws(() => transaction.validate(txBody), Error, /Inputs must be an array of size/) 41 | }) 42 | 43 | it('should fail to create a transaction with too many inputs', function () { 44 | const txBody = { 45 | inputs: [{}, {}, {}, {}, {}], 46 | outputs: [] 47 | } 48 | return assert.throws(() => transaction.validate(txBody), Error, /Inputs must be an array of size/) 49 | }) 50 | 51 | it('should fail to create a transaction with too many outputs', function () { 52 | const txBody = { 53 | inputs: [ 54 | { 55 | txindex: 0, 56 | oindex: 0, 57 | currency: '0000000000000000000000000000000000000000', 58 | blknum: 19774001, 59 | amount: 1000000000000000000 60 | } 61 | ], 62 | outputs: [{}, {}, {}, {}, {}] 63 | } 64 | return assert.throws(() => transaction.validate(txBody), Error, /Outputs must be an array of size/) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /packages/omg-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@omisego/omg-js", 3 | "version": "4.1.2-1.0.4", 4 | "description": "JavaScript Library to interact with OMG Network", 5 | "keywords": [ 6 | "OMG", 7 | "OmiseGo", 8 | "Plasma" 9 | ], 10 | "main": "src/index.js", 11 | "scripts": { 12 | "audit-check": "audit-ci --moderate" 13 | }, 14 | "author": "OmiseGo", 15 | "contributors": [ 16 | "Pong Cheecharern <@Pongch>", 17 | "Kevin Sullivan <@kevsul>", 18 | "Jarindr Thitadilaka <@jarindr>", 19 | "Nicholas Mueller <@nicholasmueller>" 20 | ], 21 | "license": "Apache-2.0", 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/omisego/omg-js.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/omisego/omg-js/issues" 28 | }, 29 | "dependencies": { 30 | "@omisego/omg-js-childchain": "4.1.2-1.0.4", 31 | "@omisego/omg-js-rootchain": "4.1.2-1.0.4", 32 | "@omisego/omg-js-util": "4.1.2-1.0.4" 33 | }, 34 | "publishConfig": { 35 | "access": "public" 36 | }, 37 | "devDependencies": { 38 | "audit-ci": "^2.5.1" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/omg-js/src/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | const RootChain = require('@omisego/omg-js-rootchain') 17 | const ChildChain = require('@omisego/omg-js-childchain') 18 | const OmgUtil = require('@omisego/omg-js-util') 19 | 20 | module.exports = { 21 | RootChain, 22 | ChildChain, 23 | OmgUtil 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-native-omg-js/hack.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Polyfill node apis introduce by web3 4 | rn-nodeify --install assert,stream,events,crypto,url,http,https,vm,os,path,process,net,zlib,_stream_transform,tls --hack 5 | -------------------------------------------------------------------------------- /packages/react-native-omg-js/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 OmiseGO Pte Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. */ 15 | 16 | require('./shim') 17 | const RootChain = require('@omisego/omg-js-rootchain') 18 | const ChildChain = require('@omisego/omg-js-childchain') 19 | const OmgUtil = require('@omisego/omg-js-util') 20 | 21 | module.exports = { 22 | RootChain, 23 | ChildChain, 24 | OmgUtil 25 | } 26 | -------------------------------------------------------------------------------- /packages/react-native-omg-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@omisego/react-native-omg-js", 3 | "version": "4.1.2-1.0.4", 4 | "description": "omg-js for React Native", 5 | "bin": { 6 | "omgjs-nodeify": "./hack.sh" 7 | }, 8 | "keywords": [ 9 | "OMG", 10 | "OmiseGo", 11 | "Plasma" 12 | ], 13 | "main": "index.js", 14 | "scripts": { 15 | "postinstall": "sh postinstall.sh", 16 | "audit-check": "audit-ci --moderate" 17 | }, 18 | "author": "OmiseGo", 19 | "contributors": [ 20 | "Pong Cheecharern <@Pongch>", 21 | "Kevin Sullivan <@kevsul>", 22 | "Jarindr Thitadilaka <@jarindr>", 23 | "Nicholas Mueller <@nicholasmueller>" 24 | ], 25 | "license": "Apache-2.0", 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/omisego/omg-js.git" 29 | }, 30 | "bugs": { 31 | "url": "https://github.com/omisego/omg-js/issues" 32 | }, 33 | "dependencies": { 34 | "@omisego/omg-js-childchain": "4.1.2-1.0.4", 35 | "@omisego/omg-js-rootchain": "4.1.2-1.0.4", 36 | "@omisego/omg-js-util": "4.1.2-1.0.4", 37 | "@tradle/react-native-http": "^2.0.1", 38 | "assert": "^1.5.0", 39 | "browserify-zlib": "^0.1.4", 40 | "events": "^1.1.1", 41 | "fast-text-encoding": "^1.0.1", 42 | "https-browserify": "0.0.1", 43 | "path-browserify": "0.0.0", 44 | "process": "^0.11.10", 45 | "react-native-crypto": "^2.2.0", 46 | "react-native-os": "^1.2.6", 47 | "react-native-randombytes": "^3.5.3", 48 | "react-native-tcp": "^3.3.2", 49 | "readable-stream": "^1.0.33", 50 | "rn-nodeify": "^10.2.0", 51 | "stream-browserify": "^1.0.0", 52 | "url": "^0.10.3", 53 | "vm-browserify": "0.0.4" 54 | }, 55 | "publishConfig": { 56 | "access": "public" 57 | }, 58 | "devDependencies": { 59 | "audit-ci": "^2.5.1" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /packages/react-native-omg-js/postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | BASE_PATH=../../@omisego 4 | JOI_PATH=node_modules/@hapi/joi/dist 5 | 6 | if [ ! -d "$BASE_PATH" ]; then 7 | exit 0 8 | fi 9 | 10 | # Polyfill TextEncoder from joi 11 | # Backup original content 12 | cat $BASE_PATH/omg-js-rootchain/$JOI_PATH/joi-browser.min.js >$BASE_PATH/omg-js-rootchain/$JOI_PATH/tmp.js 13 | cat $BASE_PATH/omg-js-childchain/$JOI_PATH/joi-browser.min.js >$BASE_PATH/omg-js-childchain/$JOI_PATH/tmp.js 14 | cat $BASE_PATH/omg-js-util/$JOI_PATH/joi-browser.min.js >$BASE_PATH/omg-js-util/$JOI_PATH/tmp.js 15 | 16 | # Replace joi-browser.min.js content 17 | echo "require('fast-text-encoding');" >$BASE_PATH/omg-js-rootchain/$JOI_PATH/joi-browser.min.js 18 | echo "require('fast-text-encoding');" >$BASE_PATH/omg-js-childchain/$JOI_PATH/joi-browser.min.js 19 | echo "require('fast-text-encoding');" >$BASE_PATH/omg-js-util/$JOI_PATH/joi-browser.min.js 20 | 21 | # Append original content 22 | cat $BASE_PATH/omg-js-rootchain/$JOI_PATH/tmp.js >>$BASE_PATH/omg-js-rootchain/$JOI_PATH/joi-browser.min.js 23 | cat $BASE_PATH/omg-js-childchain/$JOI_PATH/tmp.js >>$BASE_PATH/omg-js-childchain/$JOI_PATH/joi-browser.min.js 24 | cat $BASE_PATH/omg-js-util/$JOI_PATH/tmp.js >>$BASE_PATH/omg-js-util/$JOI_PATH/joi-browser.min.js 25 | 26 | # Remove tmp file 27 | rm $BASE_PATH/omg-js-rootchain/$JOI_PATH/tmp.js 28 | rm $BASE_PATH/omg-js-childchain/$JOI_PATH/tmp.js 29 | rm $BASE_PATH/omg-js-util/$JOI_PATH/tmp.js 30 | -------------------------------------------------------------------------------- /packages/react-native-omg-js/shim.js: -------------------------------------------------------------------------------- 1 | if (typeof __dirname === 'undefined') global.__dirname = '/' 2 | if (typeof __filename === 'undefined') global.__filename = '' 3 | if (typeof process === 'undefined') { 4 | global.process = require('process') 5 | } else { 6 | const bProcess = require('process') 7 | for (var p in bProcess) { 8 | if (!(p in process)) { 9 | process[p] = bProcess[p] 10 | } 11 | } 12 | } 13 | 14 | process.browser = false 15 | if (typeof Buffer === 'undefined') global.Buffer = require('buffer').Buffer 16 | 17 | // global.location = global.location || { port: 80 } 18 | const isDev = typeof __DEV__ === 'boolean' && __DEV__ 19 | process.env['NODE_ENV'] = isDev ? 'development' : 'production' 20 | if (typeof localStorage !== 'undefined') { 21 | localStorage.debug = isDev ? '*' : '' 22 | } 23 | 24 | // If using the crypto shim, uncomment the following line to ensure 25 | // crypto is loaded first, so it can populate global.crypto 26 | // require('crypto') 27 | --------------------------------------------------------------------------------