├── example └── app │ ├── .browserslistrc │ ├── babel.config.js │ ├── public │ ├── favicon.ico │ └── index.html │ ├── src │ ├── assets │ │ └── logo.png │ ├── main.js │ └── create.js │ ├── .gitignore │ └── README.md ├── src ├── connectProvider │ ├── web3Provider │ │ ├── helpers │ │ │ ├── providerHelpers.js │ │ │ ├── solidityTypes.js │ │ │ ├── commonGenerator.js │ │ │ ├── addressUtils.js │ │ │ ├── parseTokensData.js │ │ │ ├── webWorkerTimer │ │ │ │ └── index.js │ │ │ └── misc.js │ │ ├── index.js │ │ ├── web3-provider │ │ │ ├── methods │ │ │ │ ├── net_version.js │ │ │ │ ├── eth_coinbase.js │ │ │ │ ├── eth_requestAccounts.js │ │ │ │ ├── eth_accounts.js │ │ │ │ ├── personal_sign.js │ │ │ │ ├── eth_sign.js │ │ │ │ ├── index.js │ │ │ │ ├── utils.js │ │ │ │ ├── eth_signTypedData_v3.js │ │ │ │ ├── personal_ecRecover.js │ │ │ │ ├── eth_getEncryptionPublicKey.js │ │ │ │ ├── eth_signTypedData_v4.js │ │ │ │ ├── eth_decrypt.js │ │ │ │ ├── eth_signTransaction.js │ │ │ │ └── eth_sendTransaction.js │ │ │ ├── jsonrpc.js │ │ │ ├── middleware.js │ │ │ ├── events.js │ │ │ ├── index.js │ │ │ └── providers │ │ │ │ ├── http-request-manager.js │ │ │ │ ├── ws-provider.js │ │ │ │ └── http-provider.js │ │ ├── MEWconnect │ │ │ ├── errorHandler.js │ │ │ └── index.js │ │ ├── networks │ │ │ ├── tokens │ │ │ │ ├── tokens-etc.json │ │ │ │ ├── tokens-boba.json │ │ │ │ └── tokens-rop.json │ │ │ └── types │ │ │ │ ├── RIN.js │ │ │ │ ├── index.js │ │ │ │ ├── ETH.js │ │ │ │ ├── BOBA.js │ │ │ │ ├── BSC.js │ │ │ │ ├── MATIC.js │ │ │ │ ├── ROP.js │ │ │ │ ├── GOERLI.js │ │ │ │ ├── ETC.js │ │ │ │ └── KOV.js │ │ ├── HDWalletInterface.js │ │ ├── utils.js │ │ └── WalletInterface.js │ ├── fetchLists │ │ ├── configs.js │ │ ├── fetchMainLists.js │ │ └── index.js │ ├── utils.js │ └── platformDeepLinking.js ├── connectClient │ ├── MewConnectInitiator.js │ ├── config.js │ ├── index.js │ ├── constants │ │ ├── signals.js │ │ ├── index.js │ │ └── constants.js │ ├── MewConnectCrypto.js │ ├── MewConnectCommon.js │ └── websocketWrapper.js ├── connectWindow │ ├── images │ │ ├── closeIconBlack.js │ │ ├── close.svg │ │ ├── index.js │ │ ├── camera.js │ │ ├── refreshIcon.js │ │ ├── closeIconWhite.js │ │ └── mobile-icon.js │ ├── designTemplates │ │ ├── rawImages │ │ │ ├── appstore.png │ │ │ ├── camera.png │ │ │ ├── spaceman.png │ │ │ ├── logo-small.png │ │ │ ├── times-solid (1).png │ │ │ ├── times-solid (2).png │ │ │ ├── button-app-store.png │ │ │ └── button-google-play-color.png │ │ └── popupWindow.html │ ├── messageCreator.js │ ├── popupHtml.js │ └── popUpHandler.js ├── icon │ ├── PNG │ │ ├── c_large_mewconnect.png │ │ ├── c_medium_mewconnect.png │ │ ├── c_large_mewconnect@2x.png │ │ ├── c_large_mewconnect@3x.png │ │ ├── bw_dark_large_mewconnect.png │ │ ├── c_medium_mewconnect@2x.png │ │ ├── c_medium_mewconnect@3x.png │ │ ├── bw_dark_medium_mewconnect.png │ │ ├── bw_light_large_mewconnect.png │ │ ├── bw_light_medium_mewconnect.png │ │ ├── bw_dark_large_mewconnect@2x.png │ │ ├── bw_dark_large_mewconnect@3x.png │ │ ├── bw_dark_medium_mewconnect@2x.png │ │ ├── bw_dark_medium_mewconnect@3x.png │ │ ├── bw_light_large_mewconnect@2x.png │ │ ├── bw_light_large_mewconnect@3x.png │ │ ├── bw_light_medium_mewconnect@2x.png │ │ └── bw_light_medium_mewconnect@3x.png │ ├── App Icon │ │ ├── mew_wallet_appicon@1x.png │ │ ├── mew_wallet_appicon@2x.png │ │ └── mew_wallet_appicon@3x.png │ └── index.js ├── messageConstants.js ├── config.js ├── index.js └── messages.js ├── babel.config.js ├── .prettierrc ├── assets ├── Icon │ ├── PNG │ │ ├── c_large_mewconnect.png │ │ ├── c_large_mewconnect@2x.png │ │ ├── c_large_mewconnect@3x.png │ │ ├── c_medium_mewconnect.png │ │ ├── c_medium_mewconnect@2x.png │ │ ├── c_medium_mewconnect@3x.png │ │ ├── bw_dark_large_mewconnect.png │ │ ├── bw_dark_medium_mewconnect.png │ │ ├── bw_light_large_mewconnect.png │ │ ├── bw_light_medium_mewconnect.png │ │ ├── bw_dark_large_mewconnect@2x.png │ │ ├── bw_dark_large_mewconnect@3x.png │ │ ├── bw_dark_medium_mewconnect@2x.png │ │ ├── bw_dark_medium_mewconnect@3x.png │ │ ├── bw_light_large_mewconnect@2x.png │ │ ├── bw_light_large_mewconnect@3x.png │ │ ├── bw_light_medium_mewconnect@2x.png │ │ └── bw_light_medium_mewconnect@3x.png │ └── App Icon │ │ ├── mew_wallet_appicon@1x.png │ │ ├── mew_wallet_appicon@2x.png │ │ └── mew_wallet_appicon@3x.png ├── MEWconnect Protocol - Guide.pdf ├── forReadMe │ ├── MEWconnect Protocol - Guide.png │ ├── MEWconnect Protocol - Guide2.png │ ├── MEWconnect Protocol - Guide3.png │ └── MEWconnect Protocol - Guide4.png └── README.md ├── tests ├── browser │ ├── run.js │ ├── browser_test.html │ └── webpack.config.js └── helpers │ ├── simple-peerMock.js │ ├── signalServerMock.js │ └── MewConnectCryptoMock.js ├── README.md ├── test ├── jest.setup.js ├── config │ └── index.js ├── signals │ └── index.js ├── Unit.spec.js ├── utils │ ├── webrtc-connection.js │ ├── websocket-connection.js │ └── crypto-utils.js └── clients │ ├── initiator.js │ ├── turnReceiver.js │ └── receiver.js ├── rollup.config.js ├── .nycrc ├── jest.config.js ├── LICENSE ├── .eslintrc.js ├── .npmignore ├── .github └── workflows │ └── npm-publish.yml ├── .gitignore └── package.json /example/app/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/helpers/providerHelpers.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@babel/preset-env'] 3 | }; 4 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "trailingComma": "none" 5 | } 6 | -------------------------------------------------------------------------------- /example/app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /example/app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/example/app/public/favicon.ico -------------------------------------------------------------------------------- /example/app/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/example/app/src/assets/logo.png -------------------------------------------------------------------------------- /src/connectClient/MewConnectInitiator.js: -------------------------------------------------------------------------------- 1 | import combined from './initiator/MewConnectInitiator.js'; 2 | export default combined; 3 | -------------------------------------------------------------------------------- /src/connectWindow/images/closeIconBlack.js: -------------------------------------------------------------------------------- 1 | import closeIcon from './close.svg'; 2 | const icon = closeIcon; 3 | 4 | export default icon; 5 | -------------------------------------------------------------------------------- /src/icon/PNG/c_large_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/c_large_mewconnect.png -------------------------------------------------------------------------------- /src/icon/PNG/c_medium_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/c_medium_mewconnect.png -------------------------------------------------------------------------------- /assets/Icon/PNG/c_large_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/c_large_mewconnect.png -------------------------------------------------------------------------------- /assets/MEWconnect Protocol - Guide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/MEWconnect Protocol - Guide.pdf -------------------------------------------------------------------------------- /src/icon/PNG/c_large_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/c_large_mewconnect@2x.png -------------------------------------------------------------------------------- /src/icon/PNG/c_large_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/c_large_mewconnect@3x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/c_large_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/c_large_mewconnect@2x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/c_large_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/c_large_mewconnect@3x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/c_medium_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/c_medium_mewconnect.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_dark_large_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_dark_large_mewconnect.png -------------------------------------------------------------------------------- /src/icon/PNG/c_medium_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/c_medium_mewconnect@2x.png -------------------------------------------------------------------------------- /src/icon/PNG/c_medium_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/c_medium_mewconnect@3x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/c_medium_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/c_medium_mewconnect@2x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/c_medium_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/c_medium_mewconnect@3x.png -------------------------------------------------------------------------------- /src/icon/App Icon/mew_wallet_appicon@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/App Icon/mew_wallet_appicon@1x.png -------------------------------------------------------------------------------- /src/icon/App Icon/mew_wallet_appicon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/App Icon/mew_wallet_appicon@2x.png -------------------------------------------------------------------------------- /src/icon/App Icon/mew_wallet_appicon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/App Icon/mew_wallet_appicon@3x.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_dark_medium_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_dark_medium_mewconnect.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_light_large_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_light_large_mewconnect.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_light_medium_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_light_medium_mewconnect.png -------------------------------------------------------------------------------- /assets/Icon/App Icon/mew_wallet_appicon@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/App Icon/mew_wallet_appicon@1x.png -------------------------------------------------------------------------------- /assets/Icon/App Icon/mew_wallet_appicon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/App Icon/mew_wallet_appicon@2x.png -------------------------------------------------------------------------------- /assets/Icon/App Icon/mew_wallet_appicon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/App Icon/mew_wallet_appicon@3x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_dark_large_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_dark_large_mewconnect.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_dark_medium_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_dark_medium_mewconnect.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_light_large_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_light_large_mewconnect.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_light_medium_mewconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_light_medium_mewconnect.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_dark_large_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_dark_large_mewconnect@2x.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_dark_large_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_dark_large_mewconnect@3x.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_dark_medium_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_dark_medium_mewconnect@2x.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_dark_medium_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_dark_medium_mewconnect@3x.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_light_large_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_light_large_mewconnect@2x.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_light_large_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_light_large_mewconnect@3x.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_light_medium_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_light_medium_mewconnect@2x.png -------------------------------------------------------------------------------- /src/icon/PNG/bw_light_medium_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/icon/PNG/bw_light_medium_mewconnect@3x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_dark_large_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_dark_large_mewconnect@2x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_dark_large_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_dark_large_mewconnect@3x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_dark_medium_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_dark_medium_mewconnect@2x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_dark_medium_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_dark_medium_mewconnect@3x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_light_large_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_light_large_mewconnect@2x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_light_large_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_light_large_mewconnect@3x.png -------------------------------------------------------------------------------- /assets/forReadMe/MEWconnect Protocol - Guide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/forReadMe/MEWconnect Protocol - Guide.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_light_medium_mewconnect@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_light_medium_mewconnect@2x.png -------------------------------------------------------------------------------- /assets/Icon/PNG/bw_light_medium_mewconnect@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/Icon/PNG/bw_light_medium_mewconnect@3x.png -------------------------------------------------------------------------------- /assets/forReadMe/MEWconnect Protocol - Guide2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/forReadMe/MEWconnect Protocol - Guide2.png -------------------------------------------------------------------------------- /assets/forReadMe/MEWconnect Protocol - Guide3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/forReadMe/MEWconnect Protocol - Guide3.png -------------------------------------------------------------------------------- /assets/forReadMe/MEWconnect Protocol - Guide4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/assets/forReadMe/MEWconnect Protocol - Guide4.png -------------------------------------------------------------------------------- /src/connectWindow/designTemplates/rawImages/appstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/connectWindow/designTemplates/rawImages/appstore.png -------------------------------------------------------------------------------- /src/connectWindow/designTemplates/rawImages/camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/connectWindow/designTemplates/rawImages/camera.png -------------------------------------------------------------------------------- /src/connectWindow/designTemplates/rawImages/spaceman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/connectWindow/designTemplates/rawImages/spaceman.png -------------------------------------------------------------------------------- /src/connectWindow/designTemplates/rawImages/logo-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/connectWindow/designTemplates/rawImages/logo-small.png -------------------------------------------------------------------------------- /example/app/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | 4 | Vue.config.productionTip = false; 5 | 6 | new Vue({ 7 | render: h => h(App) 8 | }).$mount('#app'); 9 | -------------------------------------------------------------------------------- /src/connectWindow/designTemplates/rawImages/times-solid (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/connectWindow/designTemplates/rawImages/times-solid (1).png -------------------------------------------------------------------------------- /src/connectWindow/designTemplates/rawImages/times-solid (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/connectWindow/designTemplates/rawImages/times-solid (2).png -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/index.js: -------------------------------------------------------------------------------- 1 | import WalletInterface from './WalletInterface'; 2 | import { MewConnectWallet } from './MEWconnect'; 3 | 4 | export { MewConnectWallet, WalletInterface }; 5 | -------------------------------------------------------------------------------- /src/connectWindow/designTemplates/rawImages/button-app-store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/connectWindow/designTemplates/rawImages/button-app-store.png -------------------------------------------------------------------------------- /src/connectWindow/designTemplates/rawImages/button-google-play-color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/HEAD/src/connectWindow/designTemplates/rawImages/button-google-play-color.png -------------------------------------------------------------------------------- /src/connectWindow/images/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/browser/run.js: -------------------------------------------------------------------------------- 1 | const open = require('opn'); 2 | 3 | // /usr/bin/env node 4 | 5 | var separator = process.platform=="win32" ? "\\" : "/"; 6 | open(require('path').dirname(require.main.filename)+separator+".."+separator+"index.html"); 7 | -------------------------------------------------------------------------------- /src/connectProvider/fetchLists/configs.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | MAIN_LISTS_PATH: './src/connectProvider/fetchLists/lists', 3 | TOKENS_PATH: './src/connectProvider/web3Provider/networks/tokens', 4 | SUPPORTED_CHAINS: ['eth', 'rop', 'matic', 'bsc', 'etc'] 5 | }; 6 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/helpers/solidityTypes.js: -------------------------------------------------------------------------------- 1 | const uint = 'uint'; 2 | const address = 'address'; 3 | const string = 'string'; 4 | const bytes32 = 'bytes32[]'; 5 | const bytes = 'bytes'; 6 | const bool = 'bool'; 7 | 8 | export { uint, address, string, bytes32, bytes, bool }; 9 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/net_version.js: -------------------------------------------------------------------------------- 1 | import { toPayload } from '../jsonrpc'; 2 | 3 | export default async ({ payload, store }, res, next) => { 4 | if (payload.method !== 'net_version') return next(); 5 | res(null, toPayload(payload.id, store.state.network.chainID)); 6 | }; 7 | -------------------------------------------------------------------------------- /example/app/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/MEWconnect/errorHandler.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default popUpHandler => { 3 | return err => { 4 | if (err.reject) { 5 | popUpHandler.showNotice('decline'); 6 | } else { 7 | popUpHandler.showNotice('error'); 8 | console.error(err); 9 | } 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [MEWconnect Demo](https://myetherwallet.github.io/MEWconnect-web-client/#/home) 2 | 3 | 4 | **Documentation for MEWconnect-web-client can be found [here](https://myetherwallet.github.io/MEWconnect-Protocol-Documentation/)** 5 | 6 | [Previous README documentation](https://github.com/MyEtherWallet/MEWconnect-web-client/blob/master/OLD_README.md) 7 | -------------------------------------------------------------------------------- /test/jest.setup.js: -------------------------------------------------------------------------------- 1 | import 'regenerator-runtime/runtime'; 2 | const path = require('path'); 3 | function noOp() {} 4 | if (typeof window.URL.createObjectURL === 'undefined') { 5 | Object.defineProperty(window.URL, 'createObjectURL', { value: noOp }); 6 | } 7 | window.Worker = noOp; 8 | require('dotenv').config({ path: path.resolve(process.cwd(), '.env.test') }); 9 | -------------------------------------------------------------------------------- /example/app/README.md: -------------------------------------------------------------------------------- 1 | # popup 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Customize configuration 19 | See [Configuration Reference](https://cli.vuejs.org/config/). 20 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/tokens/tokens-etc.json: -------------------------------------------------------------------------------- 1 | [{"symbol":"BEC","address":"0x085fb4f24031EAedbC2B611AA528f22343eB52Db","decimals":8},{"symbol":"PLAY","address":"0x5acE17f87c7391E5792a7683069A8025B83bbd85","decimals":0},{"symbol":"UVC","address":"0x76d0184CF511788032A74a1FB91146e63F43dd53","decimals":5},{"symbol":"UVCX","address":"0xd6dF0C579f2A65049a893fDaEC9fCE098CC19F87","decimals":18}] -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/RIN.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'RIN', 3 | name_long: 'Rinkeby', 4 | homePage: 'https://www.rinkeby.io/', 5 | blockExplorerTX: 'https://rinkeby.etherscan.io/tx/[[txHash]]', 6 | blockExplorerAddr: 'https://rinkeby.etherscan.io/address/[[address]]', 7 | chainID: 4, 8 | tokens: [], 9 | contracts: [], 10 | currencyName: 'RIN' 11 | }; 12 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/index.js: -------------------------------------------------------------------------------- 1 | import ETH from './ETH.js'; 2 | import GOERLI from './GOERLI.js'; 3 | import KOV from './KOV.js'; 4 | import ROP from './ROP'; 5 | import MATIC from './MATIC'; 6 | import BSC from './BSC'; 7 | import ETC from './ETC'; 8 | import RIN from './RIN'; 9 | import BOBA from './BOBA'; 10 | export { ETH, GOERLI, KOV, ROP, MATIC, BSC, ETC, RIN, BOBA }; 11 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/helpers/commonGenerator.js: -------------------------------------------------------------------------------- 1 | import Common from 'ethereumjs-common'; 2 | 3 | const commonGenerator = network => { 4 | const customCommon = Common.forCustomChain('mainnet', { 5 | name: network.name_long, 6 | chainId: network.chainID 7 | }); 8 | return new Common(customCommon._chainParams, 'petersburg', ['petersburg']); 9 | }; 10 | 11 | export default commonGenerator; 12 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/jsonrpc.js: -------------------------------------------------------------------------------- 1 | const toPayload = (id, result) => { 2 | return { 3 | jsonrpc: '2.0', 4 | id, 5 | result 6 | }; 7 | }; 8 | const toError = (id, msg, code) => { 9 | return { 10 | jsonrpc: '2.0', 11 | id, 12 | error: { 13 | code: code ? code : -32603, 14 | message: msg 15 | } 16 | }; 17 | }; 18 | export { toPayload, toError }; 19 | -------------------------------------------------------------------------------- /src/messageConstants.js: -------------------------------------------------------------------------------- 1 | const messages = { 2 | decline: 'decline', 3 | approveTx: 'approveTx', 4 | disconnect: 'disconnect', 5 | complete: 'complete', 6 | sent: 'sent', 7 | failed: 'failed', 8 | signMessage: 'signMessage', 9 | error: 'error', 10 | notConnected: 'notConnected', 11 | declineMessage: 'declineSignMessage', 12 | communicationError: 'communicationError' 13 | }; 14 | 15 | export default messages; 16 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | const IOS_LINK = 2 | 'https://apps.apple.com/app/apple-store/id1464614025?pt=118781877&ct=mc&mt=8'; 3 | const ANDROID_LINK = 4 | 'https://play.google.com/store/apps/details?id=com.myetherwallet.mewwallet&referrer=utm_source%3Dmc'; 5 | const DISCONNECTED = 'disconnected'; 6 | const CONNECTING = 'connecting'; 7 | const CONNECTED = 'connected'; 8 | 9 | export { IOS_LINK, ANDROID_LINK, DISCONNECTED, CONNECTED, CONNECTING }; 10 | -------------------------------------------------------------------------------- /src/connectClient/config.js: -------------------------------------------------------------------------------- 1 | const packageJSON = require('../../package.json'); 2 | 3 | const env = 'production'; 4 | const version = packageJSON.version; 5 | const V1endpoint = 'https://connect.mewapi.io'; 6 | const V2endpoint = 'wss://connect2.mewapi.io/staging'; 7 | 8 | const stunServers = [ 9 | { urls: 'stun:global.stun.twilio.com:3478?transport=udp' } 10 | ]; 11 | 12 | export { env, version, stunServers, V1endpoint, V2endpoint }; 13 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/ETH.js: -------------------------------------------------------------------------------- 1 | import tokens from '../tokens/tokens-eth.json'; 2 | 3 | export default { 4 | name: 'ETH', 5 | name_long: 'Ethereum', 6 | homePage: 'https://ethereum.org', 7 | blockExplorerTX: 'https://etherscan.io/tx/[[txHash]]', 8 | blockExplorerAddr: 'https://etherscan.io/address/[[address]]', 9 | chainID: 1, 10 | tokens: tokens, 11 | contracts: [], 12 | currencyName: 'ETH' 13 | }; 14 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_coinbase.js: -------------------------------------------------------------------------------- 1 | import { toError, toPayload } from '../jsonrpc'; 2 | 3 | export default async ({ payload, store }, res, next) => { 4 | if (payload.method !== 'eth_coinbase') return next(); 5 | if (store.state.wallet) { 6 | res(null, toPayload(payload.id, store.state.wallet.getAddressString())); 7 | } else { 8 | res(toError(payload.id, 'No active wallet', 4002)); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/BOBA.js: -------------------------------------------------------------------------------- 1 | import tokens from '../tokens/tokens-boba.json'; 2 | export default { 3 | name: 'Boba', 4 | name_long: 'Boba', 5 | homePage: 'https://boba.network', 6 | blockExplorerTX: 'https://blockexplorer.boba.network/tx/[[txHash]]', 7 | blockExplorerAddr: 'https://blockexplorer.boba.network/address/[[address]]', 8 | chainID: 288, 9 | tokens: tokens, 10 | contracts: [], 11 | currencyName: 'oETH' 12 | }; 13 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/BSC.js: -------------------------------------------------------------------------------- 1 | import tokens from '../tokens/tokens-bsc.json'; 2 | export default { 3 | name: 'BSC', 4 | name_long: 'Binance Smart Chain', 5 | homePage: 'https://www.binance.org/en/smartChain', 6 | blockExplorerTX: 'https://bscscan.com/tx/[[txHash]]', 7 | blockExplorerAddr: 'https://bscscan.com/address/[[address]]', 8 | chainID: 56, 9 | tokens: tokens, 10 | contracts: [], 11 | currencyName: 'BNB' 12 | }; 13 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/MATIC.js: -------------------------------------------------------------------------------- 1 | import tokens from '../tokens/tokens-matic.json'; 2 | export default { 3 | name: 'MATIC', 4 | name_long: 'Polygon (Matic)', 5 | homePage: 'https://polygonscan.com/', 6 | blockExplorerTX: 'https://polygonscan.com/tx/[[txHash]]', 7 | blockExplorerAddr: 'https://polygonscan.com/address/[[address]]', 8 | chainID: 137, 9 | tokens: tokens, 10 | contracts: [], 11 | currencyName: 'MATIC' 12 | }; 13 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/ROP.js: -------------------------------------------------------------------------------- 1 | import tokens from '../tokens/tokens-rop.json'; 2 | 3 | export default { 4 | name: 'ROP', 5 | name_long: 'Ropsten', 6 | homePage: 'https://github.com/ethereum/ropsten', 7 | blockExplorerTX: 'https://ropsten.etherscan.io/tx/[[txHash]]', 8 | blockExplorerAddr: 'https://ropsten.etherscan.io/address/[[address]]', 9 | chainID: 3, 10 | tokens: tokens, 11 | contracts: [], 12 | currencyName: 'ROP' 13 | }; 14 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/GOERLI.js: -------------------------------------------------------------------------------- 1 | // import tokens from '@/tokens/tokens-goerli.json'; 2 | 3 | export default { 4 | name: 'GOERLI', 5 | name_long: 'Goerli', 6 | homePage: 'https://github.com/goerli/testnet', 7 | blockExplorerTX: 'https://goerli.etherscan.io/tx/[[txHash]]', 8 | blockExplorerAddr: 'https://goerli.etherscan.io/address/[[address]]', 9 | chainID: 5, 10 | tokens: [], 11 | contracts: [], 12 | currencyName: 'GöETH' 13 | }; 14 | -------------------------------------------------------------------------------- /src/connectProvider/utils.js: -------------------------------------------------------------------------------- 1 | export function isLocalStorageBlocked() { 2 | try { 3 | localStorage.getItem('test'); 4 | } catch (err) { 5 | return true; 6 | } 7 | return false; 8 | } 9 | 10 | export function postMessageToParent(message, origin = '*') { 11 | if (window.opener) { 12 | window.opener.postMessage(message, origin); 13 | return; 14 | } 15 | if (window.parent !== window) { 16 | window.parent.postMessage(message, origin); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/ETC.js: -------------------------------------------------------------------------------- 1 | import tokens from '../tokens/tokens-etc.json'; 2 | export default { 3 | name: 'ETC', 4 | name_long: 'Ethereum Classic', 5 | homePage: 'https://ethereumclassic.org/', 6 | blockExplorerTX: 'https://blockscout.com/etc/mainnet/tx/[[txHash]]', 7 | blockExplorerAddr: 'https://blockscout.com/etc/mainnet/address/[[address]]', 8 | chainID: 61, 9 | tokens: tokens, 10 | contracts: [], 11 | currencyName: 'ETC' 12 | }; 13 | -------------------------------------------------------------------------------- /test/config/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import wrtc from 'wrtc'; 4 | 5 | const stunServers = [{ urls: 'stun:global.stun.twilio.com:3478?transport=udp' }]; 6 | const websocketURL = process.env.WEBSOCKET_URL || 'wss://connect2.mewapi.io/staging' 7 | const webRTCOptions = { 8 | trickle: false, 9 | iceTransportPolicy: 'relay', 10 | config: { 11 | iceServers: stunServers 12 | }, 13 | wrtc: wrtc 14 | }; 15 | 16 | export { stunServers, websocketURL, webRTCOptions }; 17 | -------------------------------------------------------------------------------- /src/icon/index.js: -------------------------------------------------------------------------------- 1 | import bwDarkLarge from './SVG/bw_dark_large_mewconnect.svg'; 2 | import bwDarkMedium from './SVG/bw_dark_medium_mewconnect.svg'; 3 | import bwLightLarge from './SVG/bw_light_large_mewconnect.svg'; 4 | import bwLightMedium from './SVG/bw_light_medium_mewconnect.svg'; 5 | import large from './SVG/c_large_mewconnect.svg'; 6 | import medium from './SVG/c_medium_mewconnect.svg'; 7 | 8 | export default { 9 | bwDarkLarge, 10 | bwDarkMedium, 11 | bwLightLarge, 12 | bwLightMedium, 13 | large, 14 | medium 15 | }; 16 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import json from '@rollup/plugin-json'; 2 | import image from '@rollup/plugin-image'; 3 | import { babel } from '@rollup/plugin-babel'; 4 | import commonjs from '@rollup/plugin-commonjs'; 5 | export default { 6 | input: 'src/index.js', 7 | output: [ 8 | { 9 | format: 'esm', 10 | dir: 'dist/esm/' 11 | }, 12 | { format: 'cjs', dir: 'dist/cjs/' } 13 | ], 14 | plugins: [ 15 | json(), 16 | image(), 17 | commonjs({ 18 | include: /node_modules/ 19 | }), 20 | babel() 21 | ] 22 | }; 23 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/helpers/addressUtils.js: -------------------------------------------------------------------------------- 1 | import web3 from 'web3'; 2 | 3 | const isAddress = address => { 4 | if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) { 5 | return false; 6 | } else if ( 7 | /^(0x|0X)?[0-9a-f]{40}$/.test(address) || 8 | /^(0x|0X)?[0-9A-F]{40}$/.test(address) 9 | ) { 10 | return true; 11 | } 12 | return web3.utils.checkAddressChecksum(address); 13 | }; 14 | const toChecksumAddress = address => { 15 | return web3.utils.toChecksumAddress(address); 16 | }; 17 | export { isAddress, toChecksumAddress }; 18 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/middleware.js: -------------------------------------------------------------------------------- 1 | class Middleware { 2 | constructor() { 3 | this.middlewares = []; 4 | } 5 | 6 | use(fn) { 7 | this.middlewares.push(fn); 8 | } 9 | 10 | executeMiddleware(req, res, done) { 11 | this.middlewares.reduceRight( 12 | (done, next) => () => next(req, res, done), 13 | done 14 | )(req, res); 15 | } 16 | 17 | run(req, res) { 18 | return new Promise(resolve => { 19 | this.executeMiddleware(req, res, resolve); 20 | }); 21 | } 22 | } 23 | 24 | export default Middleware; 25 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/events.js: -------------------------------------------------------------------------------- 1 | export default { 2 | SHOW_WEB3_CONFIRM_MODAL: 'showWeb3Wallet', 3 | SHOW_TX_CONFIRM_MODAL: 'showTxConfirmModal', 4 | SHOW_MSG_CONFIRM_MODAL: 'showMessageConfirmModal', 5 | SHOW_TX_SIGN_MODAL: 'showTxSignModal', 6 | GET_ENCRYPTED_PUBLIC_KEY: 'eth_getEncryptionPublicKey', 7 | DECRYPT: 'eth_decrypt', 8 | SIGN_TYPE_DATA_V3: 'eth_signTypedData_v3', 9 | SIGN_TYPE_DATA_V4: 'eth_signTypedData_v4', 10 | SIGN_TYPE_DATA: 'eth_signTypedData', 11 | WALLET_NOT_CONNECTED: 'walletNotConnected', 12 | ERROR_NOTIFY: 'errorNotify' 13 | }; 14 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_requestAccounts.js: -------------------------------------------------------------------------------- 1 | import { toPayload } from '../jsonrpc'; 2 | 3 | export default async ({ payload, store }, res, next) => { 4 | if (payload.method !== 'eth_requestAccounts') return next(); 5 | if (store.state.wallet) { 6 | res(null, toPayload(payload.id, [store.state.wallet.getAddressString()])); 7 | } else { 8 | try { 9 | store.state.enable().then(accounts => { 10 | res(null, toPayload(payload.id, accounts)); 11 | }); 12 | } catch (e) { 13 | res(null, toPayload(payload.id, [])); 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/types/KOV.js: -------------------------------------------------------------------------------- 1 | // import tokens from '@/tokens/tokens-kov.json'; 2 | // import contracts from '@/contracts/contract-abi-kov.json'; 3 | // import kov from '@/assets/images/icons/network.svg'; 4 | 5 | export default { 6 | name: 'KOV', 7 | name_long: 'Kovan', 8 | homePage: 'https://kovan-testnet.github.io/website/', 9 | blockExplorerTX: 'https://kovan.etherscan.io/tx/[[txHash]]', 10 | blockExplorerAddr: 'https://kovan.etherscan.io/address/[[address]]', 11 | chainID: 42, 12 | tokens: [], 13 | contracts: [], 14 | // icon: kov, 15 | currencyName: 'KOV' 16 | }; 17 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ![guidePage1](https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/master/assets/forReadMe/MEWconnect%20Protocol%20-%20Guide.png) 4 | 5 | ![guidePage2](https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/master/assets/forReadMe/MEWconnect%20Protocol%20-%20Guide2.png) 6 | 7 | ![guidePage3](https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/master/assets/forReadMe/MEWconnect%20Protocol%20-%20Guide3.png) 8 | 9 | ![guidePage4](https://raw.githubusercontent.com/MyEtherWallet/MEWconnect-web-client/master/assets/forReadMe/MEWconnect%20Protocol%20-%20Guide4.png) -------------------------------------------------------------------------------- /src/connectClient/index.js: -------------------------------------------------------------------------------- 1 | // INITIATOR CLIENT 2 | // The initiator client is the integration end of the connection, 3 | // and sends the connection details to 4 | // the signal server which then waits for a corresponding receiver connection. 5 | 6 | // CRYPTO 7 | // the crypto constructor is a collection of methods used by both the initiator and receiver 8 | // in creating the direct connection 9 | 10 | import MewConnectInitiator from './MewConnectInitiator'; 11 | import MewConnectCrypto from './MewConnectCrypto'; 12 | 13 | export default { 14 | Crypto: MewConnectCrypto, 15 | Initiator: MewConnectInitiator 16 | }; 17 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_accounts.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | import { toPayload } from '../jsonrpc'; 4 | 5 | export default async ({ payload, store }, res, next) => { 6 | if (payload.method !== 'eth_accounts') return next(); 7 | if (store.state.wallet) { 8 | res(null, toPayload(payload.id, [store.state.wallet.getAddressString()])); 9 | } else { 10 | try { 11 | store.state.enable().then(accounts => { 12 | res(null, toPayload(payload.id, accounts)); 13 | }); 14 | } catch (e) { 15 | res(null, toPayload(payload.id, [])); 16 | } 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /src/connectWindow/images/index.js: -------------------------------------------------------------------------------- 1 | import logoMEW from './logoImage'; 2 | import logo from './mobile-icon'; 3 | import spaceman from './spaceman'; 4 | import refresh from './refreshIcon'; 5 | import playStoreButton from './button-google-play-color'; 6 | import appStoreButton from './button-app-store'; 7 | import camera from './camera'; 8 | import closeIconWhite from './closeIconWhite'; 9 | import closeIconBlack from './closeIconBlack'; 10 | 11 | export { 12 | logoMEW, 13 | logo, 14 | refresh, 15 | spaceman, 16 | playStoreButton, 17 | appStoreButton, 18 | camera, 19 | closeIconWhite, 20 | closeIconBlack 21 | }; 22 | -------------------------------------------------------------------------------- /example/app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | popup 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/connectProvider/fetchLists/fetchMainLists.js: -------------------------------------------------------------------------------- 1 | const configs = require('./configs'); 2 | const fetch = require('node-fetch'); 3 | const fs = require('fs'); 4 | 5 | const fetchLists = async () => { 6 | const tokenList = await fetch( 7 | 'https://api.github.com/repos/MyEtherWallet/ethereum-lists/contents/dist/tokens' 8 | ) 9 | .then(res => res.json()) 10 | .catch(console.error); 11 | fs.writeFileSync( 12 | configs.MAIN_LISTS_PATH + '/tokens.json', 13 | JSON.stringify(tokenList) 14 | ); 15 | }; 16 | 17 | (async () => { 18 | try { 19 | await fetchLists(); 20 | console.log('Done'); 21 | } catch (e) { 22 | console.error(e); 23 | } 24 | })(); 25 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/index.js: -------------------------------------------------------------------------------- 1 | import WSProvider from './providers/ws-provider'; 2 | import HttpProvider from './providers/http-provider'; 3 | class MEWProvider { 4 | constructor(host, options, store, eventHub) { 5 | if (host && typeof host === 'string') { 6 | if (/^http(s)?:\/\//i.test(host)) { 7 | store.noSubs = true; 8 | return new HttpProvider(host, options, store, eventHub); 9 | } else if (/^ws(s)?:\/\//i.test(host)) { 10 | return new WSProvider(host, options, store, eventHub); 11 | } else if (host) { 12 | throw new Error('Can\'t autodetect provider for "' + host + '"'); 13 | } 14 | } 15 | } 16 | } 17 | 18 | export default MEWProvider; 19 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "description": "These are just examples for demonstration, nothing prescriptive", 3 | "nyc": { 4 | "check-coverage": true, 5 | "per-file": true, 6 | "lines": 99, 7 | "statements": 99, 8 | "functions": 99, 9 | "branches": 99, 10 | "include": [ 11 | "src/**/*.js" 12 | ], 13 | "exclude": [ 14 | "src/**/*.spec.js" 15 | ], 16 | "ignore-class-method": "methodToIgnore", 17 | "reporter": [ 18 | "lcov", 19 | "text-summary" 20 | ], 21 | "require": [ 22 | "./test/helpers/some-helper.js" 23 | ], 24 | "extension": [ 25 | ".jsx" 26 | ], 27 | "cache": true, 28 | "all": true, 29 | "temp-directory": "./alternative-tmp", 30 | "report-dir": "./alternative" 31 | } 32 | } -------------------------------------------------------------------------------- /tests/browser/browser_test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha Tests 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // INITIATOR CLIENT 2 | // The initiator client is the integration end of the connection, 3 | // and sends the connection details to 4 | // the signal server which then waits for a corresponding receiver connection. 5 | 6 | // CRYPTO 7 | // the crypto constructor is a collection of methods used by both the initiator and receiver 8 | // in creating the direct connection 9 | import 'core-js/stable'; 10 | import 'regenerator-runtime/runtime'; 11 | import MewConnectClient from './connectClient/index'; 12 | import MewConnectProvider from './connectProvider/index'; 13 | // import icons from './icon'; 14 | 15 | export default { 16 | Initiator: MewConnectClient.Initiator, 17 | Crypto: MewConnectClient.Crypto, 18 | Client: MewConnectClient, 19 | Provider: MewConnectProvider 20 | // icons 21 | }; 22 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/personal_sign.js: -------------------------------------------------------------------------------- 1 | import EventNames from '../events'; 2 | import { toError, toPayload } from '../jsonrpc'; 3 | import misc from '../../helpers/misc'; 4 | import debugLogger from 'debug'; 5 | const debug = debugLogger('MEWconnectWeb3'); 6 | 7 | export default async ({ payload, eventHub }, res, next) => { 8 | if (payload.method !== 'personal_sign') return next(); 9 | const msg = payload.params[0]; 10 | eventHub.emit(EventNames.SHOW_MSG_CONFIRM_MODAL, msg, _response => { 11 | if (_response.reject) { 12 | debug('USER DECLINED PERSONAL SIGN'); 13 | res(toError(payload.id, 'User Rejected Request', 4001)); 14 | return; 15 | } 16 | _response = misc.sanitizeHex(_response.toString('hex')); 17 | res(null, toPayload(payload.id, _response)); 18 | }); 19 | }; 20 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | verbose: true, 3 | /* clearMocks: true, 4 | collectCoverage: true, 5 | setTimeout: 10000, 6 | coverageDirectory: 'coverage',*/ 7 | testEnvironment: 'jsdom', 8 | testMatch: ['**/__tests__/**/*.js?(x)', '**/tests/**/?(*.)+(spec|test).js?(x)', '**/test/**/?(*.)+(spec|test).js?(x)'], 9 | testPathIgnorePatterns: ['/node_modules/'], 10 | // "testEnvironment": "node", 11 | /* transform: { 12 | "^.+\\.(js) ? $": "babel-jest" 13 | },*/ 14 | "setupFiles": [ 15 | "/test/jest.setup.js" 16 | ], 17 | "moduleNameMapper": { 18 | "@signals(.*)$": "/test/signals$1", 19 | "@clients(.*)$": "/test/clients$1", 20 | "@config(.*)$": "/test/config$1", 21 | "@utils(.*)$": "/test/utils$1", 22 | "@/(.*)$": "dist$1" 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_sign.js: -------------------------------------------------------------------------------- 1 | import EventNames from '../events'; 2 | import { toError, toPayload } from '../jsonrpc'; 3 | import misc from '../../helpers/misc'; 4 | import debugLogger from 'debug'; 5 | const debug = debugLogger('MEWconnectWeb3'); 6 | 7 | export default async ({ payload, eventHub }, res, next) => { 8 | if (payload.method !== 'eth_sign') return next(); 9 | const msg = payload.params[1]; 10 | eventHub.emit(EventNames.SHOW_MSG_CONFIRM_MODAL, msg, _response => { 11 | if (_response.reject) { 12 | debug('USER DECLINED SIGN MESSAGE'); 13 | res(toError(payload.id, 'User Rejected Request', 4001)); 14 | return; 15 | } 16 | _response = misc.sanitizeHex(_response.toString('hex')); 17 | debug('sign result', _response); 18 | res(null, toPayload(payload.id, _response)); 19 | }); 20 | }; 21 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/HDWalletInterface.js: -------------------------------------------------------------------------------- 1 | import WalletInterface from './WalletInterface'; 2 | 3 | class HDWalletInterface extends WalletInterface { 4 | constructor( 5 | path, 6 | pubkey, 7 | isHardware, 8 | identifier, 9 | errorHandler, 10 | txSigner, 11 | msgSigner, 12 | displayAddress 13 | ) { 14 | super(pubkey, true, identifier); 15 | this.path = path; 16 | this.txSigner = txSigner; 17 | this.msgSigner = msgSigner; 18 | this.isHardware = isHardware; 19 | this.errorHandler = errorHandler; 20 | this.displayAddress = displayAddress; 21 | } 22 | 23 | signTransaction(txParams) { 24 | return super.signTransaction(txParams, this.txSigner); 25 | } 26 | 27 | signMessage(msg) { 28 | return super.signMessage(msg, this.msgSigner); 29 | } 30 | } 31 | 32 | export default HDWalletInterface; 33 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/providers/http-request-manager.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Manager as Web3RequestManager } from 'web3-core-requestmanager'; 3 | class HttpRequestManager { 4 | constructor(host, options) { 5 | options = options || {}; 6 | this.host = host; 7 | const config = { 8 | timeout: options.timeout || 15000, 9 | headers: { 'Content-Type': 'application/json' } 10 | }; 11 | if (options.headers) { 12 | options.headers.forEach(header => { 13 | config.headers[header.name] = header.value; 14 | }); 15 | } 16 | this.request = axios.create(config); 17 | return new Web3RequestManager(this); 18 | } 19 | send(payload, callback) { 20 | this.request 21 | .post(this.host, payload) 22 | .then(result => { 23 | callback(null, result.data); 24 | }) 25 | .catch(err => { 26 | callback(err); 27 | }); 28 | } 29 | disconnect() {} 30 | } 31 | export default HttpRequestManager; 32 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/index.js: -------------------------------------------------------------------------------- 1 | import ethSendTransaction from './eth_sendTransaction'; 2 | import ethSign from './eth_sign'; 3 | import ethAccounts from './eth_accounts'; 4 | import ethCoinbase from './eth_coinbase'; 5 | import ethSignTransaction from './eth_signTransaction'; 6 | import netVersion from './net_version'; 7 | import personalSign from './personal_sign'; 8 | import ecRecover from './personal_ecRecover'; 9 | import getEncryptionPublicKey from './eth_getEncryptionPublicKey'; 10 | import decrypt from './eth_decrypt'; 11 | import signTypedData_v3 from './eth_signTypedData_v3'; 12 | import signTypedData_v4 from './eth_signTypedData_v4'; 13 | import ethRequestAccounts from './eth_requestAccounts'; 14 | 15 | export { 16 | ethSendTransaction, 17 | ethSign, 18 | personalSign, 19 | ecRecover, 20 | ethAccounts, 21 | ethCoinbase, 22 | ethSignTransaction, 23 | netVersion, 24 | getEncryptionPublicKey, 25 | decrypt, 26 | signTypedData_v3, 27 | signTypedData_v4, 28 | ethRequestAccounts 29 | }; 30 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/utils.js: -------------------------------------------------------------------------------- 1 | import { formatters } from 'web3-core-helpers'; 2 | 3 | const getSanitizedTx = tx => { 4 | return new Promise((resolve, reject) => { 5 | if (!tx.gas && !tx.gasLimit && !tx.chainId) 6 | return reject(new Error('"gas" or "chainId" is missing')); 7 | if ( 8 | tx.nonce < 0 || 9 | tx.gas < 0 || 10 | tx.gasPrice < 0 || 11 | !tx.gasPrice || 12 | tx.chainId < 0 13 | ) 14 | return reject( 15 | new Error( 16 | 'Gas, gasPrice, nonce or chainId is lower than 0 or "gasPrice" is missing ' 17 | ) 18 | ); 19 | 20 | try { 21 | tx = formatters.inputCallFormatter(tx); 22 | const transaction = tx; 23 | if (tx.to) transaction.to = tx.to; 24 | transaction.data = tx.data || '0x'; 25 | transaction.value = tx.value || '0x'; 26 | transaction.chainId = tx.chainId; 27 | resolve(transaction); 28 | } catch (e) { 29 | reject(e); 30 | } 31 | }); 32 | }; 33 | ``; 34 | export { getSanitizedTx }; 35 | -------------------------------------------------------------------------------- /src/connectWindow/messageCreator.js: -------------------------------------------------------------------------------- 1 | import { messages } from '../messages'; 2 | export function getMessage(text, extra) { 3 | if (extra) { 4 | switch (extra.type) { 5 | case 'sent': 6 | return `${ 7 | messages[extra.type] 8 | }
View details`; 12 | case 'failed': 13 | return `${ 14 | messages[extra.type] 15 | }
View details`; 19 | case 'nonStandardMessage': 20 | return extra.message; 21 | } 22 | } 23 | 24 | const regEx = new RegExp(/^Returned error:/); 25 | if (regEx.test(text)) { 26 | return text; 27 | } 28 | 29 | if (!text) { 30 | return messages.defaultMessage; 31 | } 32 | 33 | return messages[text]; 34 | } 35 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_signTypedData_v3.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { toError, toPayload } from '../jsonrpc'; 3 | import EventNames from '../events'; 4 | import debugLogger from 'debug'; 5 | const debug = debugLogger('MEWconnectWeb3'); 6 | const debugErrors = debugLogger('MEWconnectError'); 7 | 8 | export default async ({ payload, eventHub }, res, next) => { 9 | if (payload.method !== 'eth_signTypedData_v3') return next(); 10 | try { 11 | eventHub.emit( 12 | EventNames.SIGN_TYPE_DATA_V3, 13 | payload.params[1], 14 | _response => { 15 | if (_response.reject) { 16 | debug('USER DECLINED SIGN TYPED DATA'); 17 | res(toError(payload.id, 'User Rejected Request', 4001)); 18 | return; 19 | } 20 | debug('eth_signTypedData_v3 response', payload.method, _response); 21 | res(null, toPayload(payload.id, _response)); 22 | } 23 | ); 24 | } catch (e) { 25 | debugErrors(e); 26 | debugErrors('Error: eth_signTypedData_v3', e); 27 | res(e); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/personal_ecRecover.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import misc from '../../helpers/misc'; 3 | import { toError, toPayload } from '../jsonrpc'; 4 | import utils from 'ethereumjs-utils'; 5 | 6 | export default async ({ payload }, res, next) => { 7 | if (payload.method !== 'personal_ecRecover') return next(); 8 | if (payload.params.length < 2) { 9 | res( 10 | toError( 11 | payload.id, 12 | `personal_ecRecover expects 2 parameters. Received ${payload.params.length} ` 13 | ) 14 | ); 15 | } 16 | const parts = utils.fromRpcSig(payload.params[1]); 17 | if (!parts) { 18 | res( 19 | toError(payload.id, 'Invalid signature supplied to personal_ecRecover') 20 | ); 21 | } 22 | const recovered = utils.ecrecover( 23 | utils.hashPersonalMessage(misc.toBuffer(payload.params[0])), 24 | parts.v, 25 | parts.r, 26 | parts.s 27 | ); 28 | const addressBuffer = utils.pubToAddress(recovered); 29 | 30 | res(null, toPayload(payload.id, '0x' + addressBuffer.toString('hex'))); 31 | }; 32 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/tokens/tokens-boba.json: -------------------------------------------------------------------------------- 1 | [{"symbol":"OMG","address":"0xe1E2ec9a85C607092668789581251115bCBD20de","decimals":18},{"symbol":"USDT", 2 | "address":"0x5DE1677344D3Cb0D7D465c10b72A8f60699C062d","decimals":6},{"symbol":"DAI","address":"0xf74195Bb8a5cf652411867c5C2C5b8C2a402be35", 3 | "decimals":18},{"symbol":"USDC","address":"0x66a2A913e447d6b4BF33EFbec43aAeF87890FBbc","decimals":6},{"symbol":"WBTC", 4 | "address":"0xdc0486f8bf31DF57a952bcd3c1d3e166e3d9eC8b","decimals":8},{"symbol":"REPv2","address":"0x8b5B1E971862015bc058234FC11ce6C4a4c536dD", 5 | "decimals":18},{"symbol":"BAT","address":"0xc0C16dF1ee7dcEFb88C55003C49F57AA416A3578","decimals":18},{"symbol":"ZRX", 6 | "address":"0xf135f13Db3B114107dCB0B32B6c9e10fFF5a6987","decimals":18},{"symbol":"SUSHI","address":"0x5fFccc55C0d2fd6D3AC32C26C020B3267e933F1b", 7 | "decimals":18},{"symbol":"LINK","address":"0xD5D5030831eE83e22a2C9a5cF99931A50c676433","decimals":18},{"symbol":"UNI", 8 | "address":"0xDBDE1347fED5dC03C74059010D571a16417d307e","decimals":18},{"symbol":"DODO","address":"0x572c5B5BF34f75FB62c39b9BFE9A75bb0bb47984", 9 | "decimals":18}] -------------------------------------------------------------------------------- /test/signals/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const signals = { 4 | attemptingTurn: 'attemptingturn', 5 | turnToken: 'turntoken', 6 | tryTurn: 'tryturn', 7 | connect: 'connect', 8 | connection: 'connection', 9 | signature: 'signature', 10 | offerSignal: 'offersignal', 11 | offer: 'offer', 12 | answerSignal: 'answersignal', 13 | answer: 'answer', 14 | initiated: 'initiated', 15 | rtcConnected: 'rtcconnected', 16 | disconnect: 'disconnect', 17 | handshake: 'handshake', 18 | confirmation: 'confirmation', 19 | socketTimeout: 'socketTimeout', 20 | invalidConnection: 'InvalidConnection', 21 | confirmationFailedBusy: 'confirmationFailedBusy', 22 | confirmationFailed: 'confirmationFailed', 23 | receivedSignal: 'receivedSignal', 24 | error: 'error' 25 | } 26 | 27 | const roles = { 28 | initiator: 'initiator', 29 | receiver: 'receiver' 30 | } 31 | 32 | const rtcSignals = { 33 | error: 'error', 34 | connect: 'connect', 35 | disconnected: 'disconnected', 36 | close: 'close', 37 | data: 'data', 38 | signal: 'signal' 39 | } 40 | 41 | export { signals, roles, rtcSignals } 42 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_getEncryptionPublicKey.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { toError, toPayload } from '../jsonrpc'; 3 | import EventNames from '../events'; 4 | import debugLogger from 'debug'; 5 | const debug = debugLogger('MEWconnectWeb3'); 6 | const debugErrors = debugLogger('MEWconnectError'); 7 | 8 | export default async ({ payload, eventHub }, res, next) => { 9 | if (payload.method !== 'eth_getEncryptionPublicKey') return next(); 10 | try { 11 | eventHub.emit( 12 | EventNames.GET_ENCRYPTED_PUBLIC_KEY, 13 | payload.params, 14 | _response => { 15 | if (_response.reject) { 16 | debug('USER DECLINED SIGN TRANSACTION'); 17 | res(toError(payload.id, 'User Rejected Request', 4001)); 18 | return; 19 | } 20 | debug('eth_getEncryptionPublicKey response', payload.method, _response); 21 | res(null, toPayload(payload.id, _response)); 22 | } 23 | ); 24 | } catch (e) { 25 | debugErrors(e); 26 | debugErrors('Error: eth_getEncryptionPublicKey', e); 27 | res(e); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 MyEtherWallet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_signTypedData_v4.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { toError, toPayload } from '../jsonrpc'; 3 | import EventNames from '../events'; 4 | import debugLogger from 'debug'; 5 | const debug = debugLogger('MEWconnectWeb3'); 6 | const debugErrors = debugLogger('MEWconnectError'); 7 | 8 | export default async ({ payload, eventHub }, res, next) => { 9 | if ( 10 | payload.method !== 'eth_signTypedData_v4' && 11 | payload.method !== 'eth_signTypedData' 12 | ) 13 | return next(); 14 | try { 15 | eventHub.emit( 16 | EventNames.SIGN_TYPE_DATA_V4, 17 | payload.params[1], 18 | _response => { 19 | if (_response.reject) { 20 | debug('USER DECLINED SIGN TYPED DATA'); 21 | res(toError(payload.id, 'User Rejected Request', 4001)); 22 | return; 23 | } 24 | debug('eth_signTypedData_v4 response', payload.method, _response); 25 | res(null, toPayload(payload.id, _response)); 26 | } 27 | ); 28 | } catch (e) { 29 | debugErrors(e); 30 | debugErrors('Error: eth_signTypedData_v3', e); 31 | res(e); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /src/connectWindow/popupHtml.js: -------------------------------------------------------------------------------- 1 | const noticeHtml = (elementId, imageSrc, iconImage) => { 2 | return ` 3 |
4 |
5 | 6 |
7 | 8 | MEW wallet 9 |
10 | 11 |
12 | 13 |
14 | `; 15 | }; 16 | 17 | const connectedNoticeHtml = (elementId, imageSrc, iconImage) => { 18 | return ` 19 | 20 |
21 |
22 | 23 |
24 | 25 |
26 | Connected to 27 | MEW wallet 28 | Powered by MyEtherWallet 29 |
30 |
31 | `; 32 | }; 33 | 34 | export { noticeHtml, connectedNoticeHtml }; 35 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | globals: { 3 | VERSION: 'readonly', 4 | ROUTER_MODE: 'readonly', 5 | NODE_ENV: 'readonly' 6 | }, 7 | ignorePatterns: ['dist/**/*.js'], 8 | root: true, 9 | env: { 10 | node: true, 11 | jest: true 12 | }, 13 | extends: ['plugin:vue/recommended', '@vue/prettier', 'eslint:recommended'], 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 17 | 'no-else-return': ['error', { allowElseIf: true }], 18 | 'arrow-parens': 'off', 19 | 'generator-star-spacing': 'off', 20 | semi: 'off', 21 | 'prefer-const': 'error', 22 | 'no-var': 'error', 23 | 'security/detect-new-buffer': 'off', 24 | 'security/detect-object-injection': 'off', 25 | 'require-atomic-updates': 'off', 26 | 'no-prototype-builtins': 'off', 27 | 'no-irregular-whitespace': [ 28 | 'error', 29 | { 30 | skipComments: true, 31 | skipTemplates: true, 32 | skipStrings: true, 33 | skipRegExps: true 34 | } 35 | ], 36 | 'vue/custom-event-name-casing': 'off' 37 | }, 38 | parserOptions: { 39 | parser: 'babel-eslint' 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /src/messages.js: -------------------------------------------------------------------------------- 1 | const messages = { 2 | decline: 'User declined action in MEW wallet app', 3 | approveTx: 'Check your phone to approve transaction ', 4 | disconnect: 'Disconnected from MEW wallet', 5 | complete: 'Transaction completed', 6 | sent: 'Transaction sent', 7 | failed: 'Transaction failed', 8 | signMessage: 'Check your phone to sign the message', 9 | declineSignMessage: 'User declined message signing', 10 | notConnected: 'Phone not connected. Please connect your phone and try again', 11 | defaultMessage: 'Check your phone to continue', 12 | error: 'An error occurred while preparing the last action', 13 | communicationError: 14 | 'Could not complete last response from MEW wallet. Nothing was sent. Please try to send or sign again.', 15 | disconnectError: '' 16 | }; 17 | 18 | const messageConstants = { 19 | decline: 'decline', 20 | approveTx: 'approveTx', 21 | disconnect: 'disconnect', 22 | complete: 'complete', 23 | sent: 'sent', 24 | failed: 'failed', 25 | signMessage: 'signMessage', 26 | error: 'error', 27 | notConnected: 'notConnected', 28 | declineMessage: 'declineSignMessage', 29 | communicationError: 'communicationError', 30 | disconnectError: 'disconnectError' 31 | }; 32 | 33 | export { messages, messageConstants }; 34 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | .idea/ 61 | /example/Initiator_Peer/mewConnect.js 62 | /example/Initiator_Peer/mewConnectUtils.js 63 | /example/Initiator_Peer/mewRTC.js 64 | /browser/package.json 65 | /example/ 66 | /site/ 67 | /tests/fsdfds/ 68 | /assets/ 69 | /scraps/ 70 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-node@v1 16 | with: 17 | node-version: 12 18 | - run: npm ci 19 | 20 | publish-npm: 21 | needs: build 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v2 25 | - uses: actions/setup-node@v1 26 | with: 27 | node-version: 12 28 | registry-url: https://registry.npmjs.org/ 29 | - run: npm ci 30 | - run: npm publish 31 | env: 32 | NODE_AUTH_TOKEN: ${{secrets.npm_token}} 33 | 34 | # publish-gpr: 35 | # needs: build 36 | # runs-on: ubuntu-latest 37 | # steps: 38 | # - uses: actions/checkout@v2 39 | # - uses: actions/setup-node@v1 40 | # with: 41 | # node-version: 12 42 | # registry-url: https://npm.pkg.github.com/ 43 | # - run: npm ci 44 | # - run: npm publish 45 | # env: 46 | # NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}} 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | .idea/ 61 | /example/Initiator_Peer/mewConnect.js 62 | /example/Initiator_Peer/mewConnectUtils.js 63 | /example/Initiator_Peer/mewRTC.js 64 | /browser/package.json 65 | /example/node_modules/ 66 | /tests/fsdfds/ 67 | /test/ganache.sh 68 | /scraps/ 69 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_decrypt.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import { toError, toPayload } from '../jsonrpc'; 3 | import EventNames from '../events'; 4 | import debugLogger from 'debug'; 5 | import { isHexStrict } from 'web3-utils'; 6 | import { bufferToHex } from 'ethereumjs-utils'; 7 | const debug = debugLogger('MEWconnectWeb3'); 8 | const debugErrors = debugLogger('MEWconnectError'); 9 | 10 | export default async ({ payload, eventHub }, res, next) => { 11 | if (payload.method !== 'eth_decrypt') return next(); 12 | try { 13 | debug(payload.params[0]); // todo remove dev item 14 | let jsonObj = payload.params[0]; 15 | if (!isHexStrict(jsonObj)) { 16 | jsonObj = JSON.parse(jsonObj); 17 | jsonObj = bufferToHex(Buffer.from(JSON.stringify(jsonObj), 'utf8')); 18 | } 19 | eventHub.emit( 20 | EventNames.DECRYPT, 21 | [jsonObj, payload.params[1]], 22 | _response => { 23 | if (_response.reject) { 24 | debug('USER DECLINED SIGN TRANSACTION'); 25 | res(toError(payload.id, 'User Rejected Request', 4001)); 26 | return; 27 | } 28 | debug('decrypt response', payload.method, _response); 29 | res(null, toPayload(payload.id, _response)); 30 | } 31 | ); 32 | } catch (e) { 33 | debugErrors(e); 34 | debugErrors('Error: eth_decrypt', e); 35 | res(e); 36 | } 37 | }; 38 | -------------------------------------------------------------------------------- /example/app/src/create.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | export default class MewWalletConnector { 4 | constructor(url) { 5 | this.mewConnect = ''; 6 | this.provider = ''; 7 | this.url = url; 8 | } 9 | 10 | async activate() { 11 | let account; 12 | const { default: MewConnect } = await import('../../../src'); 13 | if (!MewConnect.Provider.isConnected) { 14 | this.mewConnect = new MewConnect.Provider({ windowClosedError: true }); 15 | 16 | this.provider = this.mewConnect.makeWeb3Provider(1, this.url, true); 17 | this.mewConnect.on('disconnected', () => { 18 | // this.emitDeactivate(); 19 | }); 20 | account = await this.mewConnect 21 | .enable() 22 | .catch(() => { 23 | throw new Error('The user rejected the request.'); 24 | }) 25 | .then(accounts => accounts[0]); 26 | } 27 | 28 | 29 | 30 | return { provider: this.provider, chainId: 1, account: account }; 31 | } 32 | 33 | async getProvider(){ 34 | return this.provider; 35 | } 36 | 37 | async getChainId() { 38 | return 1; 39 | } 40 | 41 | async getAccount() { 42 | return this.provider.send('eth_accounts').then(accounts => accounts[0]); 43 | } 44 | 45 | deactivate() { 46 | this.provider.close(); 47 | // this.emitDeactivate(); 48 | } 49 | 50 | async close() { 51 | this.provider.close(); 52 | // this.emitDeactivate(); 53 | } 54 | } -------------------------------------------------------------------------------- /src/connectClient/constants/signals.js: -------------------------------------------------------------------------------- 1 | const signalV1 = { 2 | attemptingTurn: 'attemptingTurn', 3 | turnToken: 'turnToken', 4 | tryTurn: 'tryTurn', 5 | connection: 'connection', 6 | connect: 'connect', 7 | signature: 'signature', 8 | offerSignal: 'offerSignal', 9 | offer: 'offer', 10 | answerSignal: 'answerSignal', 11 | answer: 'answer', 12 | rtcConnected: 'rtcConnected', 13 | disconnect: 'disconnect', 14 | handshake: 'handshake', 15 | confirmation: 'confirmation', 16 | invalidConnection: 'InvalidConnection', 17 | confirmationFailedBusy: 'confirmationFailedBusy', 18 | confirmationFailed: 'confirmationFailed' 19 | }; 20 | 21 | const signalV2 = { 22 | //V1 (ish) 23 | attemptingTurn: 'attemptingturn', 24 | turnToken: 'turntoken', 25 | tryTurn: 'tryturn', 26 | connect: 'connect', 27 | connection: 'connection', 28 | signature: 'signature', 29 | offerSignal: 'offersignal', 30 | offer: 'offer', 31 | answerSignal: 'answersignal', 32 | answer: 'answer', 33 | rtcConnected: 'rtcconnected', 34 | disconnect: 'disconnect', 35 | handshake: 'handshake', 36 | confirmation: 'confirmation', 37 | invalidConnection: 'InvalidConnection', 38 | confirmationFailedBusy: 'confirmationFailedBusy', 39 | confirmationFailed: 'confirmationFailed', 40 | // V2 41 | initiated: 'initiated', 42 | socketTimeout: 'socketTimeout', 43 | receivedSignal: 'receivedSignal', 44 | error: 'error', 45 | disconnected: 'disconnected' 46 | }; 47 | 48 | export { signalV1, signalV2 }; 49 | -------------------------------------------------------------------------------- /src/connectClient/constants/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | version, 3 | versions, 4 | connectionCodeSchemas, 5 | connectionCodeSeparator, 6 | stages, 7 | rtc, 8 | iceConnectionState, 9 | RTCSignalingState, 10 | RTCIceGatheringState, 11 | lifeCycle, 12 | communicationTypes, 13 | loggerLevels 14 | } from './constants'; 15 | 16 | import { signalV1, signalV2 } from './signals'; 17 | import { V2endpoint, V1endpoint } from '../config'; 18 | 19 | const signalUrl = { 20 | V1: V1endpoint, 21 | V2: V2endpoint 22 | }; 23 | 24 | const signals = { 25 | V1: signalV1, 26 | V2: signalV2 27 | }; 28 | 29 | const versionIdentify = ver => { 30 | const parts = ver.toString().split('.'); 31 | if (parts.length > 0) { 32 | ver = parts[0]; 33 | } 34 | switch (ver) { 35 | case 1: 36 | case '1': 37 | case 'V1': 38 | return 'V1'; 39 | case 2: 40 | case '2': 41 | case 'V2': 42 | return 'V2'; 43 | default: 44 | return 'V2'; 45 | } 46 | }; 47 | 48 | const signalServer = ver => { 49 | return signalUrl[versionIdentify(ver)]; 50 | }; 51 | 52 | const signal = ver => { 53 | return signals[versionIdentify(ver)]; 54 | }; 55 | 56 | export { 57 | version, 58 | versions, 59 | signalServer, 60 | connectionCodeSchemas, 61 | connectionCodeSeparator, 62 | signals, 63 | signal, 64 | stages, 65 | rtc, 66 | iceConnectionState, 67 | RTCSignalingState, 68 | RTCIceGatheringState, 69 | lifeCycle, 70 | communicationTypes, 71 | loggerLevels 72 | }; 73 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/helpers/parseTokensData.js: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bignumber.js'; 2 | 3 | function parseTokensData(data, to, _web3, networkToken, networkName) { 4 | const web3 = _web3; 5 | let token = networkToken.find(el => { 6 | return el.address.toLowerCase() === to.toLowerCase(); 7 | }); 8 | 9 | const jsonInterface = { 10 | constant: false, 11 | inputs: [ 12 | { name: '_to', type: 'address' }, 13 | { name: '_amount', type: 'uint256' } 14 | ], 15 | name: 'transfer', 16 | outputs: [{ name: '', type: 'bool' }], 17 | payable: false, 18 | stateMutability: 'nonpayable', 19 | type: 'function' 20 | }; 21 | const transferFuncSig = web3.eth.abi.encodeFunctionSignature(jsonInterface); 22 | const tokenData = { 23 | tokenTransferTo: '', 24 | tokenTransferVal: '', 25 | tokenSymbol: '' 26 | }; 27 | if (data.substr(0, 10) === transferFuncSig) { 28 | const params = web3.eth.abi.decodeParameters( 29 | ['address', 'uint256'], 30 | `${data.substr(10)}` 31 | ); 32 | const value = new BigNumber(params[1]); 33 | tokenData.tokenTransferTo = params[0]; 34 | tokenData.tokenTransferVal = token 35 | ? value 36 | .div(new BigNumber(10).pow(token.decimals)) 37 | .toFixed() 38 | .toString() 39 | : value.toString(); 40 | tokenData.tokenSymbol = token ? token.symbol : 'Unidentified Token'; 41 | } 42 | 43 | return tokenData; 44 | } 45 | 46 | export default parseTokensData; 47 | -------------------------------------------------------------------------------- /tests/helpers/simple-peerMock.js: -------------------------------------------------------------------------------- 1 | // socket.io-client.js 2 | let EVENTS = {}; 3 | 4 | export default class PeerMock { 5 | constructor(toEmit, data, time) { 6 | this._pc = { 7 | addEventListener(event, args) { 8 | console.log(event, args); // todo remove dev item 9 | EVENTS[event].forEach(func => func(args)); 10 | } 11 | }; 12 | } 13 | 14 | Peer() { 15 | let _this = this; 16 | console.log('called'); // todo remove dev item 17 | return function (){ 18 | console.log('called'); // todo remove dev item 19 | return _this; 20 | } 21 | }; 22 | 23 | on(event, func) { 24 | console.log(event); // todo remove dev item 25 | if (EVENTS[event]) { 26 | return EVENTS[event].push(func); 27 | } 28 | EVENTS[event] = [func]; 29 | // if (event === toEmit) { 30 | setTimeout(() => { 31 | console.log(event, data); // todo remove dev item 32 | EVENTS[event].forEach(func => func(data)); 33 | }, 50); 34 | // } 35 | } 36 | 37 | emit(event) { 38 | setTimeout(() => { 39 | console.log(event, data); // todo remove dev item 40 | EVENTS[event].forEach(func => func(data)); 41 | }, time); 42 | } 43 | 44 | signal() { 45 | setTimeout(() => { 46 | console.log(event, data); // todo remove dev item 47 | EVENTS[event].forEach(func => func(data)); 48 | }, time); 49 | } 50 | 51 | send() { 52 | 53 | } 54 | 55 | destroy() { 56 | 57 | } 58 | 59 | } 60 | 61 | // cleanup helper 62 | export function cleanup() { 63 | EVENTS = {}; 64 | } 65 | 66 | -------------------------------------------------------------------------------- /tests/helpers/signalServerMock.js: -------------------------------------------------------------------------------- 1 | // socket.io-client.js 2 | let EVENTS = {}; 3 | 4 | 5 | const socket = { 6 | on(event, func) { 7 | if (EVENTS[event]) { 8 | return EVENTS[event].push(func); 9 | } 10 | EVENTS[event] = [func]; 11 | }, 12 | emit(event, args) { 13 | console.log(event, args); // todo remove dev item 14 | EVENTS[event].forEach(func => func(args)); 15 | } 16 | }; 17 | 18 | const connect = { 19 | connect: function(url, options) { 20 | console.log(url, options); // todo remove dev item 21 | return socket; 22 | } 23 | }; 24 | 25 | export default function io(url, options) { 26 | return connect; 27 | }; 28 | 29 | 30 | 31 | 32 | // function emit(event, ...args) { 33 | // EVENTS[event].forEach(func => func(...args)); 34 | // } 35 | // 36 | // const socket = { 37 | // on(event, func) { 38 | // if (EVENTS[event]) { 39 | // return EVENTS[event].push(func); 40 | // } 41 | // EVENTS[event] = [func]; 42 | // }, 43 | // emit 44 | // }; 45 | // 46 | // function connect(url, options){ 47 | // return socket; 48 | // } 49 | // 50 | // function io(url, options) { 51 | // console.log(url, option); // todo remove dev item 52 | // return { 53 | // connect 54 | // } 55 | // } 56 | 57 | // io.connect = (url, options) => { 58 | // console.log(url, option); // todo remove dev item 59 | // return socket; 60 | // }; 61 | 62 | // Additional helpers, not included in the real socket.io-client,just for out test. 63 | // to emulate server emit. 64 | // export const serverSocket = { emit, connect }; 65 | 66 | // cleanup helper 67 | export function cleanup() { 68 | EVENTS = {}; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /tests/browser/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | 4 | module.exports = { 5 | mode: 'none', // "production" | "development" | "none" 6 | entry: { 7 | MewConnectBrowserTest: [/*'babel-polyfil', */'../specs/MewConnectInitiator.spec.js'] 8 | }, 9 | output: { 10 | path: __dirname, // string 11 | filename: '[name].min.js', 12 | // library: 'MewConnect', // string, 13 | // the name of the exported library 14 | // TODO figure out the best option to use here 15 | // libraryTarget: 'umd', // umd', // "var" | "assign" | "this" | "window" | "global" | "commonjs" | "commonjs2" | "amd" | "umd" 16 | // the type of the exported library 17 | // globalObject: 'this' 18 | }, 19 | module: { 20 | rules: [ 21 | { 22 | test: /\.js$/, 23 | exclude: /(node_modules|bower_components|dist)/, 24 | use: { 25 | loader: 'babel-loader', 26 | options: { 27 | plugins: [require('babel-plugin-transform-object-rest-spread')], 28 | presets: ["env"] 29 | } 30 | } 31 | } 32 | ] 33 | }, 34 | resolve: { 35 | modules: [ 36 | 'node_modules', 37 | path.resolve(__dirname, 'src') 38 | ], 39 | extensions: ['.js', '.json', '.jsx', '.css'] 40 | }, 41 | performance: { 42 | hints: 'warning', // enum 43 | maxAssetSize: 200000, // int (in bytes), 44 | maxEntrypointSize: 400000 // int (in bytes) 45 | }, 46 | devtool: 'source-map', // enum 47 | context: __dirname, // string (absolute path!) 48 | target: 'web', // enum 49 | // the environment in which the bundle should run 50 | plugins: [ 51 | new webpack.DefinePlugin({'process.env.NODE_ENV': JSON.stringify('production')}) 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /test/Unit.spec.js: -------------------------------------------------------------------------------- 1 | // import 'regenerator-runtime/runtime'; 2 | // import { expect as chaiExpect } from 'chai'; 3 | // import debug from 'debug'; 4 | // import * as MewConnectSrc from '../../src'; 5 | // import MewConnectReceiver from '../helpers/MewConnectReceiver'; 6 | // 7 | // const connectLogger = debug('test:Connect'); 8 | // const fallbackLogger = debug('test:Fallback'); 9 | // 10 | // const signalUrl = typeof signalServer !== 'undefined' ? signalServer : 'https://connect.mewapi.io'; 11 | import MewConnectInitiator from '../src/connectClient/initiator/MewConnectInitiator'; 12 | import MewConnectCrypto from '../src/connectClient/MewConnectCrypto'; 13 | 14 | describe('Check Base Connection Operation', () => { 15 | it('tests', async done => { 16 | const init = new MewConnectInitiator(); 17 | init.generateKeys(); 18 | const mewCrypto = init.mewCrypto 19 | const data = { type: 'address', data: '0x2khkj223lkjh2', id: '123' }; 20 | const input = JSON.stringify(data); 21 | 22 | const encrypt = async arg => { 23 | let encryptedSend; 24 | if (typeof arg === 'string') { 25 | encryptedSend = await mewCrypto.encrypt(arg); 26 | } else { 27 | encryptedSend = await mewCrypto.encrypt(JSON.stringify(arg)); 28 | } 29 | return JSON.stringify(encryptedSend); 30 | }; 31 | encrypt(input).then(vlaue => { 32 | init.webRtcCommunication.on('appData', result => { 33 | console.log(result); // todo remove dev item 34 | expect(result.type).toEqual(expect.stringMatching(data.type)); 35 | expect(result.data).toEqual(expect.stringMatching(data.data)); 36 | expect(result.id).toEqual(expect.stringMatching(data.id)); 37 | done(); 38 | }) 39 | init.webRtcCommunication.onData('123', vlaue) 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/connectProvider/fetchLists/index.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const fs = require('fs'); 3 | const configs = require('./configs'); 4 | const tokenList = require('./lists/tokens.json'); 5 | 6 | const fetchTokens = async () => { 7 | try { 8 | if (!fs.existsSync(configs.TOKENS_PATH)) { 9 | fs.mkdirSync(configs.TOKENS_PATH); 10 | } 11 | const tokenFileURL = 12 | 'https://cdn.jsdelivr.net/gh/MyEtherWallet/ethereum-lists@master/dist/tokens/'; 13 | if (tokenList !== undefined && tokenList.length > 0) { 14 | for (let i = 0; i < tokenList.length; i++) { 15 | const tokenFile = tokenList[i]; 16 | if (configs.SUPPORTED_CHAINS.includes(tokenFile.name)) { 17 | let tokensCollection = await fetch( 18 | `${tokenFileURL + tokenFile.name}/tokens-${tokenFile.name}.json` 19 | ) 20 | .then(res => res.json()) 21 | .catch(err => console.log(err)); 22 | if (tokensCollection !== undefined) { 23 | console.log('Writing tokens for the network: ' + tokenFile.name); 24 | tokensCollection = tokensCollection.map(item => { 25 | return { 26 | symbol: item.symbol, 27 | address: item.address, 28 | decimals: item.decimals 29 | }; 30 | }); 31 | fs.writeFileSync( 32 | `${configs.TOKENS_PATH}/tokens-${tokenFile.name}.json`, 33 | JSON.stringify(tokensCollection) 34 | ); 35 | } 36 | } 37 | } 38 | } 39 | } catch (e) { 40 | console.error(e); // Not captured by sentry 41 | } 42 | }; 43 | 44 | const run = async () => { 45 | await fetchTokens(); 46 | }; 47 | 48 | (async () => { 49 | try { 50 | await run(); 51 | console.log('Done'); 52 | } catch (e) { 53 | console.error(e); 54 | } 55 | })(); 56 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/utils.js: -------------------------------------------------------------------------------- 1 | import { bufferToInt } from 'ethereumjs-utils'; 2 | 3 | const getBufferFromHex = hex => { 4 | hex = sanitizeHex(hex); 5 | const _hex = hex.toLowerCase().replace('0x', ''); 6 | return new Buffer(_hex, 'hex'); 7 | }; 8 | const padLeftEven = hex => { 9 | hex = hex.length % 2 != 0 ? '0' + hex : hex; 10 | return hex; 11 | }; 12 | const sanitizeHex = hex => { 13 | hex = hex.substring(0, 2) == '0x' ? hex.substring(2) : hex; 14 | if (hex == '') return ''; 15 | return '0x' + padLeftEven(hex); 16 | }; 17 | const bufferToHex = buffer => { 18 | return '0x' + buffer.toString('hex'); 19 | }; 20 | const getHexTxObject = tx => { 21 | return { 22 | to: sanitizeHex(tx.to.toString('hex')), 23 | value: sanitizeHex(tx.value.toString('hex')), 24 | data: sanitizeHex(tx.data.toString('hex')), 25 | chainId: tx.getChainId(), 26 | nonce: sanitizeHex(tx.nonce.toString('hex')), 27 | gasLimit: sanitizeHex(tx.gasLimit.toString('hex')), 28 | gasPrice: sanitizeHex(tx.gasPrice.toString('hex')) 29 | }; 30 | }; 31 | const getSignTransactionObject = tx => { 32 | return { 33 | rawTransaction: bufferToHex(tx.serialize()), 34 | tx: { 35 | nonce: bufferToHex(tx.nonce), 36 | gasPrice: bufferToHex(tx.gasPrice), 37 | gas: tx.gasLimit ? bufferToHex(tx.gasLimit) : bufferToHex(tx.gas), 38 | to: bufferToHex(tx.to), 39 | value: bufferToHex(tx.value), 40 | input: bufferToHex(tx.data), 41 | v: bufferToHex(tx.v), 42 | r: bufferToHex(tx.r), 43 | s: bufferToHex(tx.s), 44 | hash: bufferToHex(tx.hash()) 45 | } 46 | }; 47 | }; 48 | const calculateChainIdFromV = v => { 49 | const sigV = bufferToInt(v); 50 | let chainId = Math.floor((sigV - 35) / 2); 51 | if (chainId < 0) chainId = 0; 52 | return chainId; 53 | }; 54 | export { 55 | getBufferFromHex, 56 | bufferToHex, 57 | getSignTransactionObject, 58 | sanitizeHex, 59 | padLeftEven, 60 | getHexTxObject, 61 | calculateChainIdFromV 62 | }; 63 | -------------------------------------------------------------------------------- /src/connectWindow/images/camera.js: -------------------------------------------------------------------------------- 1 | const image = `data:image/png;base64, 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`; 2 | 3 | export default image; 4 | -------------------------------------------------------------------------------- /tests/helpers/MewConnectCryptoMock.js: -------------------------------------------------------------------------------- 1 | import ethUtils from 'ethereumjs-utils'; 2 | import crypto from 'crypto'; 3 | import secp256k1 from 'secp256k1'; 4 | 5 | const privateStr = 6 | '6d96ff74ad12467b3ebd993f6b0927d6996d464900bbd63de866b3090cb36d2b'; 7 | const pubStr = 8 | '035892d28938ebe58339649fdaef273953b0b1f5d68bfb7422adf41d5993d9e20d'; 9 | export default class MewConnectCrypto { 10 | static create() { 11 | return new MewConnectCrypto(); 12 | } 13 | 14 | setPrivate(pvtKey) { 15 | this.prvt = Buffer.from(pvtKey, 'hex'); 16 | } 17 | 18 | generateMessage() { 19 | return crypto.randomBytes(32).toString('hex'); 20 | } 21 | 22 | // Not for the Address, but generate them for the connection check 23 | prepareKey() { 24 | this.prvt = Buffer.from(privateStr, 'hex'); 25 | this.pub = Buffer.from(pubStr, 'hex'); 26 | return { pub: this.pub, pvt: this.prvt }; 27 | } 28 | 29 | generatePrivate() { 30 | return Buffer.from(privateStr, 'hex'); 31 | } 32 | 33 | generatePublic() { 34 | const pvt = Buffer.from(privateStr, 'hex'); 35 | this.prvt = pvt; 36 | return secp256k1.publicKeyCreate(pvt); 37 | } 38 | 39 | encrypt(dataToSend) { 40 | return new Promise(resolve => { 41 | resolve(dataToSend); 42 | }); 43 | } 44 | 45 | decrypt(dataToSee) { 46 | return new Promise(resolve => { 47 | resolve(dataToSee); 48 | }); 49 | } 50 | 51 | signMessage(msgToSign) { 52 | return new Promise((resolve, reject) => { 53 | try { 54 | const msg = ethUtils.hashPersonalMessage(ethUtils.toBuffer(msgToSign)); 55 | const signed = ethUtils.ecsign( 56 | Buffer.from(msg), 57 | Buffer.from(this.prvt, 'hex') 58 | ); 59 | const combined = Buffer.concat([ 60 | Buffer.from([signed.v]), 61 | Buffer.from(signed.r), 62 | Buffer.from(signed.s) 63 | ]); 64 | const combinedHex = combined.toString('hex'); 65 | resolve(combinedHex); 66 | } catch (e) { 67 | reject(e); 68 | } 69 | }); 70 | } 71 | 72 | bufferToConnId(buf) { 73 | return buf.toString('hex').slice(32); 74 | } 75 | 76 | isJSON(arg) { 77 | try { 78 | JSON.parse(arg); 79 | return true; 80 | } catch (e) { 81 | return false; 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/connectWindow/images/refreshIcon.js: -------------------------------------------------------------------------------- 1 | const refreshIcon = 2 | 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMThweCIgaGVpZ2h0PSIxN3B4IiB2aWV3Qm94PSIwIDAgMTggMTciIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDQ5LjIgKDUxMTYwKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5leGNoYW5nZSAoMSk8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZGVmcz48L2RlZnM+CiAgICA8ZyBpZD0iU3ltYm9scyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9ImFkZHJlc3MtYm94IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTkwLjAwMDAwMCwgLTEwNy4wMDAwMDApIiBmaWxsPSIjMDAwMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0iI2ZmZmZmZiIgc3Ryb2tlLXdpZHRoPSIwLjIiPgogICAgICAgICAgICA8ZyBpZD0iZXhjaGFuZ2UtKDEpIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxOTEuMDAwMDAwLCAxMDguMDAwMDAwKSI+CiAgICAgICAgICAgICAgICA8cGF0aCBkPSJNMTUuMDY2OTg5OCw5LjMwNTY0NTY4IEMxNS4wNjY5ODk4LDEwLjY2ODM5NzEgMTQuMjIzMjYxMiwxMC43NjU3MzY1IDE0LjA2MjU1MSwxMC43NjU3MzY1IEwxLjcxMjk3NjE4LDEwLjc2NTczNjUgTDQuMzY5NzE2NzYsOC4xOTExMDk2NyBMMy42NjE1ODc0Miw3LjUgTDAuMTQ2MDUxNjc3LDEwLjkwNjg3ODcgQy0wLjA0ODY4Mzg5MjIsMTEuMDk2NzIwOSAtMC4wNDg2ODM4OTIyLDExLjQwMzI3OTEgMC4xNDYwNTE2NzcsMTEuNTkzMTIxMyBMMy42NjE1ODc0MiwxNSBMNC4zNjk3MTY3NiwxNC4zMTM3NTczIEwxLjcxMjk3NjE4LDExLjczOTEzMDQgTDE0LjA2MjU1MSwxMS43MzkxMzA0IEMxNC43NTU2MTM4LDExLjczOTEzMDQgMTYuMDcxNDI4NiwxMS4yNTI0MzM1IDE2LjA3MTQyODYsOS4zMDU2NDU2OCBMMTYuMDcxNDI4Niw3Ljg0NTU1NDgzIEwxNS4wNjY5ODk4LDcuODQ1NTU0ODMgTDE1LjA2Njk4OTgsOS4zMDU2NDU2OCBaIiBpZD0iU2hhcGUiPjwvcGF0aD4KICAgICAgICAgICAgICAgIDxwYXRoIGQ9Ik0xLjAwNDQzODc4LDUuNjk5MjIxMjggQzEuMDA0NDM4NzgsNC4zMzY0Njk4MiAxLjg0ODE2NzM2LDQuMjM5MTMwNDMgMi4wMDg4Nzc1Niw0LjIzOTEzMDQzIEwxNC4zNTg0NTI0LDQuMjM5MTMwNDMgTDExLjcwMTcxMTgsNi44MTM3NTczIEwxMi40MDk4NDEyLDcuNSBMMTUuOTI1Mzc2OSw0LjA5MzEyMTM1IEMxNi4xMjAxMTI1LDMuOTAzMjc5MTIgMTYuMTIwMTEyNSwzLjU5NjcyMDg4IDE1LjkyNTM3NjksMy40MDY4Nzg2NSBMMTIuNDA5ODQxMiwwIEwxMS42OTY2ODk2LDAuNjkxMTA5NjY5IEwxNC4zNTg0NTI0LDMuMjY1NzM2NTMgTDIuMDA4ODc3NTYsMy4yNjU3MzY1MyBDMS4zMTU4MTQ4LDMuMjY1NzM2NTMgMCwzLjc1MjQzMzQ4IDAsNS42OTkyMjEyOCBMMCw3LjE1OTMxMjEzIEwxLjAwNDQzODc4LDcuMTU5MzEyMTMgTDEuMDA0NDM4NzgsNS42OTkyMjEyOCBaIiBpZD0iU2hhcGUiPjwvcGF0aD4KICAgICAgICAgICAgPC9nPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+'; 3 | 4 | export default refreshIcon; 5 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/networks/tokens/tokens-rop.json: -------------------------------------------------------------------------------- 1 | [{"symbol":"*PLASMA","address":"0x95D7321EdCe519419ba1DbC60A89bAfbF55EAC0D","decimals":6},{"symbol":"aBAT","address":"0x0D0Ff1C81F2Fbc8cbafA8Df4bF668f5ba963Dab4","decimals":18},{"symbol":"ABCD","address":"0x21C968b15be143484E0BD1CfaE1f7378f89b414c","decimals":18},{"symbol":"aDAI","address":"0xcB1Fe6F440c49E9290c3eb7f158534c2dC374201","decimals":18},{"symbol":"aETH","address":"0x2433A1b6FcF156956599280C3Eb1863247CFE675","decimals":18},{"symbol":"aKNC","address":"0xCf6efd4528d27Df440fdd585a116D3c1fC5aDdEe","decimals":18},{"symbol":"aLEND","address":"0x383261d0e287f0A641322AEB15E3da50147Dd36b","decimals":18},{"symbol":"aLINK","address":"0x52fd99c15e6FFf8D4CF1B83b2263a501FDd78973","decimals":18},{"symbol":"aMANA","address":"0x8e96a4068da80F66ef1CFc7987f0F834c26106fa","decimals":18},{"symbol":"aMKR","address":"0xEd6A5d671f7c55aa029cbAEa2e5E9A18E9d6a1CE","decimals":18},{"symbol":"aREP","address":"0xE4B92BcDB2f972e1ccc069D4dB33d5f6363738dE","decimals":18},{"symbol":"aSUSD","address":"0x5D17e0ea2d886F865E40176D71dbc0b59a54d8c1","decimals":6},{"symbol":"aTUSD","address":"0x6308180b412c481982628D093f342A259b4e681C","decimals":18},{"symbol":"aUSDC","address":"0x2dB6a31f973Ec26F5e17895f0741BB5965d5Ae15","decimals":6},{"symbol":"aUSDT","address":"0x790744bC4257B4a0519a3C5649Ac1d16DDaFAE0D","decimals":6},{"symbol":"aWBTC","address":"0xA1c4dB01F8344eCb11219714706C82f0c0c64841","decimals":18},{"symbol":"aZRX","address":"0x5BDC773c9D3515a5e3Dd415428F92a90E8e63Ae4","decimals":18},{"symbol":"cBAT","address":"0x189CA88bE39C9c1B8c8dd437F5ff1DB1f584b14b","decimals":8},{"symbol":"cDAI","address":"0x2B536482a01E620eE111747F8334B395a42A555E","decimals":8},{"symbol":"cETH","address":"0x42a628e0c5F3767930097B34b08dCF77e78e4F2B","decimals":8},{"symbol":"cREP","address":"0xA3C2c1618214549281E1b15dee9D682C8aa0DC1C","decimals":8},{"symbol":"cUSDC","address":"0x43a1363AFB28235720FCbDF0C2dAb7759091F7e0","decimals":8},{"symbol":"cWBTC","address":"0x06E728D7907C164649427D2ACFD4c81669D453Bf","decimals":8},{"symbol":"cZRX","address":"0xDff375162cfE7D77473C1BEC4560dEDE974E138c","decimals":8},{"symbol":"dqr30","address":"0xa1bAccA0e12D4091Ec1f92e7CaE3394CC9854D3D","decimals":18},{"symbol":"FQXT","address":"0x5D47033e140f7b589CC2545416eC9D7a712A7de9","decimals":8},{"symbol":"MEWV5","address":"0x4C572Fbc03D4A2B683cF4f10ffdcaFD00885E108","decimals":9},{"symbol":"RLC","address":"0x7314Dc4d7794b5E7894212CA1556ae8e3De58621","decimals":9}] -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_signTransaction.js: -------------------------------------------------------------------------------- 1 | // import unit from 'ethjs-unit'; 2 | import { toError, toPayload } from '../jsonrpc'; 3 | import EventNames from '../events'; 4 | import { getSanitizedTx } from './utils'; 5 | 6 | import debugLogger from 'debug'; 7 | import BigNumber from 'bignumber.js'; 8 | const debug = debugLogger('MEWconnectWeb3'); 9 | const debugErrors = debugLogger('MEWconnectError'); 10 | 11 | export default async ({ payload, store, eventHub }, res, next) => { 12 | if (payload.method !== 'eth_signTransaction') return next(); 13 | const tx = payload.params[0]; 14 | const localTx = Object.assign({}, tx); 15 | delete localTx['gas']; 16 | delete localTx['nonce']; 17 | try { 18 | if (!store.state.wallet) { 19 | eventHub.emit(EventNames.WALLET_NOT_CONNECTED); 20 | debug('NOT ACTIVE WALLET IDENTIFIED'); 21 | res(toError(payload.id, 'No active wallet: eth_signTransaction', 4002)); 22 | return; 23 | } 24 | tx.nonce = !tx.nonce 25 | ? await store.state.web3.eth.getTransactionCount( 26 | store.state.wallet.getAddressString() 27 | ) 28 | : tx.nonce; 29 | 30 | if (tx.gasLimit && !tx.gas) { 31 | tx.gas = tx.gasLimit; 32 | } else if (!tx.gasLimit && tx.gas) { 33 | tx.gasLimit = tx.gas; 34 | } 35 | tx.gas = 36 | !tx.gas || new BigNumber(tx.gas).lte(0) 37 | ? await store.state.web3.eth.estimateGas(localTx) 38 | : tx.gas; 39 | tx.chainId = !tx.chainId ? store.state.network.chainID : tx.chainId; 40 | tx.gasPrice = 41 | !tx.gasPrice || new BigNumber(tx.gasPrice).lte(0) 42 | ? await store.state.web3.eth.getGasPrice() 43 | : tx.gasPrice; 44 | 45 | getSanitizedTx(tx) 46 | .then(_tx => { 47 | eventHub.emit(EventNames.SHOW_TX_SIGN_MODAL, _tx, _response => { 48 | if (_response.reject) { 49 | debug('USER DECLINED SIGN TRANSACTION'); 50 | res(toError(payload.id, 'User Rejected Request', 4001)); 51 | return; 52 | } 53 | debug('return signed transaction', payload.method, _response); 54 | res(null, toPayload(payload.id, _response.rawTransaction)); 55 | }); 56 | }) 57 | .catch(e => { 58 | eventHub.emit(EventNames.ERROR_NOTIFY, e); 59 | debugErrors('Error: eth_signTransaction', e); 60 | res(e); 61 | }); 62 | } catch (e) { 63 | debugErrors(e); 64 | debugErrors('Error: eth_signTransaction', e); 65 | res(e); 66 | } 67 | }; 68 | -------------------------------------------------------------------------------- /src/connectClient/constants/constants.js: -------------------------------------------------------------------------------- 1 | import { version } from '../config'; 2 | 3 | const versions = ['0.0.1']; 4 | 5 | const connectionCodeSchemas = { 6 | '0.0.1': ['version', 'key', 'connId'] 7 | }; 8 | 9 | const connectionCodeSeparator = '_'; 10 | 11 | const rtc = { 12 | error: 'error', 13 | connect: 'connect', 14 | close: 'close', 15 | data: 'data', 16 | signal: 'signal' 17 | }; 18 | 19 | const iceConnectionState = { 20 | new: 'new', 21 | connecting: 'connecting', 22 | connected: 'connected', 23 | disconnected: 'disconnected', 24 | failed: 'failed', 25 | closed: 'closed' 26 | }; 27 | 28 | const RTCSignalingState = { 29 | stable: 'stable', 30 | 'have-local-offer': 'have-local-offer', 31 | 'have-remote-offer': 'have-remote-offer', 32 | 'have-local-pranswer': 'have-local-pranswer', 33 | 'have-remote-pranswer': 'have-remote-pranswer' 34 | }; 35 | 36 | const RTCIceGatheringState = { 37 | new: 'new', 38 | gathering: 'gathering', 39 | complete: 'complete' 40 | }; 41 | 42 | const stages = { 43 | initiator: 'initiator', 44 | receiver: 'receiver' 45 | }; 46 | 47 | const lifeCycle = { 48 | AuthRejected: 'AuthRejected', 49 | RtcInitiatedEvent: 'RtcInitiatedEvent', 50 | signatureCheck: 'signatureCheck', 51 | SocketConnectedEvent: 'SocketConnectedEvent', 52 | confirmationFailedEvent: 'confirmationFailedEvent', 53 | confirmationFailedBusyEvent: 'confirmationFailedBusyEvent', 54 | invalidConnectionEvent: 'invalidConnectionEvent', 55 | codeDisplay: 'codeDisplay', 56 | checkNumber: 'checkNumber', 57 | ConnectionId: 'ConnectionId', 58 | receiverVersion: 'receiverVersion', 59 | offerCreated: 'offerCreated', 60 | sendOffer: 'sendOffer', 61 | answerReceived: 'answerReceived', 62 | RtcConnectedEvent: 'RtcConnectedEvent', 63 | RtcConnectedEmitted: 'RtcConnectedEmitted', 64 | RtcClosedEvent: 'RtcClosedEvent', 65 | RtcDisconnectEvent: 'RtcDisconnectEvent', 66 | RtcDestroyedEvent: 'RtcDestroyedEvent', 67 | RtcFailedEvent: 'RtcFailedEvent', 68 | RtcErrorEvent: 'RtcErrorEvent', 69 | UsingFallback: 'UsingFallback', 70 | Failed: 'failed', 71 | attemptedDisconnectedSend: 'attemptedDisconnectedSend', 72 | connected: 'connected', 73 | disconnected: 'disconnected', 74 | ShowReload: 'ShowReload', 75 | decryptError: 'decryptError', 76 | connectingStart: 'connectingStart' 77 | }; 78 | 79 | const communicationTypes = { 80 | address: 'address', 81 | signMessage: 'signMessage', 82 | signTx: 'signTx' 83 | }; 84 | 85 | const loggerLevels = { 86 | errorLvl: 'error', 87 | warnLvl: 'warn', 88 | infoLvl: 'info', 89 | verboseLvl: 'verbose', 90 | debugLvl: 'debug', 91 | sillyLvl: 'silly' 92 | }; 93 | 94 | export { 95 | version, 96 | versions, 97 | connectionCodeSchemas, 98 | connectionCodeSeparator, 99 | stages, 100 | rtc, 101 | iceConnectionState, 102 | RTCSignalingState, 103 | RTCIceGatheringState, 104 | lifeCycle, 105 | communicationTypes, 106 | loggerLevels 107 | }; 108 | -------------------------------------------------------------------------------- /test/utils/webrtc-connection.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import Peer from 'simple-peer' 4 | import wrtc from 'wrtc' 5 | import { stunServers, turnServers } from '@config' 6 | import { rtcSignals } from '@signals' 7 | 8 | export default class WebRTCConnection { 9 | constructor(options = {}) { 10 | this.options = options 11 | this.peer = {} 12 | this.listeners = {} 13 | } 14 | 15 | /** 16 | * Attempt to initiate an "offer" WebRTC connection between two peers. 17 | * This will return an offer object that can be used by the receiver to create a 18 | * p2p connection. 19 | * 20 | * If ICE servers are given, then use those instead. The object format returned 21 | * by twilio is incorrect. The 'url' properties must be renamed 'urls' 22 | * 23 | * @return {Object} - WebRTC connection offer 24 | */ 25 | async offer(iceServers = null) { 26 | return new Promise((resolve, reject) => { 27 | const options = { 28 | initiator: true, 29 | trickle: false, 30 | iceTransportPolicy: 'relay', 31 | config: { 32 | iceServers: iceServers 33 | ? iceServers.map(obj => { 34 | const newObject = {} 35 | delete Object.assign(newObject, obj, { ['urls']: obj['url'] })[ 36 | 'url' 37 | ] 38 | return newObject 39 | }) 40 | : stunServers 41 | }, 42 | wrtc: wrtc 43 | } 44 | 45 | this.peer = new Peer(options) 46 | this.peer.on(rtcSignals.signal, data => { 47 | resolve(data) 48 | }) 49 | 50 | }) 51 | } 52 | 53 | /** 54 | * Given a WebRTC offer object (created with the offer() function), 55 | * a receiver can create a WebRTC response in order to create a p2p 56 | * connection between the initiator and receiver. 57 | * 58 | * @param {Object} offer - WebRTC offer object create with offer() 59 | * @return {Object} - WebRTC answer object, to be used by the initiator 60 | */ 61 | async answer(offer, iceServers = null) { 62 | return new Promise((resolve, reject) => { 63 | const options = { 64 | trickle: false, 65 | iceTransportPolicy: 'relay', 66 | config: { 67 | iceServers: iceServers ? iceServers : stunServers 68 | }, 69 | wrtc: wrtc 70 | } 71 | this.peer = new Peer(options) 72 | this.peer.on(rtcSignals.error, console.log) 73 | this.peer.signal(offer) 74 | this.peer.on(rtcSignals.signal, data => { 75 | resolve(data) 76 | }) 77 | }) 78 | } 79 | 80 | /** 81 | * Given a WebRTC answer object, complete WebRTC connection. 82 | * @param {Object} answer - WebRTC answer object created by answer() 83 | */ 84 | connect(answer) { 85 | this.peer.signal(answer) 86 | } 87 | 88 | /** 89 | * Disconnect from current WebRTC connection 90 | */ 91 | disconnect() { 92 | this.peer.destroy() 93 | } 94 | 95 | /** 96 | * On @sigal event sent via WebRTC, perform given fn 97 | * @param {String} signal - WebRTC signal/event. E.g. 'data' 98 | * @param {Function} fn - Callback function to perform on signal event 99 | */ 100 | on(signal, fn) { 101 | this.peer.on(signal, fn) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/connectProvider/platformDeepLinking.js: -------------------------------------------------------------------------------- 1 | import { IOS_LINK, ANDROID_LINK } from '../config'; 2 | 3 | const APP_STORE_LINK = IOS_LINK; 4 | const PLAY_STORE_LINK = ANDROID_LINK; 5 | const SCHEMA_BASE = 'mewwallet://dapps'; 6 | 7 | export function mobileCheck() { 8 | let check = false; 9 | (function(a) { 10 | // eslint-disable-next-line 11 | if ( 12 | /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test( 13 | a 14 | ) || 15 | // eslint-disable-next-line 16 | /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test( 17 | a.substr(0, 4) 18 | ) 19 | ) 20 | check = true; 21 | })(navigator.userAgent || navigator.vendor || window.opera); 22 | return check; 23 | } 24 | function iOS() { 25 | return ['iPhone Simulator', 'iPhone', 'iPod'].includes(navigator.platform); 26 | } 27 | 28 | export function nativeCheck() { 29 | return new Promise(resolve => { 30 | try { 31 | if (mobileCheck()) { 32 | const fallbackToStore = () => { 33 | if (iOS()) { 34 | window.location.replace(APP_STORE_LINK); 35 | } else { 36 | window.location.replace(PLAY_STORE_LINK); 37 | } 38 | resolve(false); 39 | }; 40 | const openApp = () => { 41 | const loc = window.location.origin; 42 | const url = encodeURIComponent(loc); 43 | const scheme = `${SCHEMA_BASE}?url=${url}`; 44 | window.location.replace(scheme); 45 | }; 46 | if (iOS()) { 47 | return resolve(true); 48 | } 49 | const triggerAppOpen = () => { 50 | openApp(); 51 | setTimeout(fallbackToStore, 1000); 52 | }; 53 | triggerAppOpen(); 54 | } else { 55 | resolve(true); 56 | } 57 | } catch (e) { 58 | resolve(true); 59 | } 60 | }); 61 | } 62 | -------------------------------------------------------------------------------- /test/utils/websocket-connection.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import queryString from 'query-string' 4 | import webSocketClient from 'promise-ws' 5 | 6 | export default class WebsocketConnection { 7 | constructor(options = {}) { 8 | this.options = options 9 | this.socket = {} 10 | this.listeners = {} 11 | } 12 | 13 | /** 14 | * Connect to a given @websocketURL with given @options query params. 15 | * The client will connect and bind every on "message" event to the 16 | * onMessage() function. 17 | * 18 | * In order to handle a message, the on() member function must be used to add 19 | * an listener. This gives functionality similar to socket.io, in which 20 | * it is possible to so something like: 21 | * 22 | * socket.on('error', err => {}) 23 | * as opposed to: 24 | * socket.on('message', message => { if (message.signal === 'error')... }) 25 | * 26 | * @param {String} websocketURL - WSS address of websocket API 27 | * @param {Object} options - JSON-formatted connection query params 28 | * @param {String} options.role - Either "initiator" or "receiver" accordingly 29 | * @param {String} options.connId - Last 32 characters of the public key portion of the key-pair 30 | * created for the particular paired connection 31 | * @param {String} options.signed - Private key signed with the private key created for the connection 32 | */ 33 | async connect(websocketUrl, options = {}) { 34 | let url = `${websocketUrl}?${queryString.stringify(options)}` 35 | this.socket = await webSocketClient.create(url) 36 | this.socket.on('message', this.onMessage.bind(this)) 37 | } 38 | 39 | /** 40 | * On 'message' event, parse the message and if possible, 41 | * call the event listener with the message's particular signal. 42 | * 43 | * Messages are received as stringified JSON objects, that when parsed, 44 | * take the following format: 45 | * 46 | * { 47 | * signal, // This is the signal that the member function on() can bind to 48 | * data, // The actual data payload 49 | * message // Accompanying server message 50 | * } 51 | * 52 | * @param {String} message - Stringified JSON payload sent by the server 53 | * @return {[type]} [description] 54 | */ 55 | onMessage(message) { 56 | const parsedMessage = JSON.parse(message) 57 | const signal = parsedMessage.signal 58 | const data = parsedMessage.data 59 | 60 | try { 61 | this.listeners[signal].call(this, data) 62 | } catch (e) { 63 | // Unhandled message signal 64 | } 65 | } 66 | 67 | /** 68 | * Bind an function to a particular message signal event. 69 | * E.G. 70 | * socket.on('error', err => {}) 71 | * 72 | * @param {String} signal - The signal to listen for 73 | * @param {Function} fn - Function to perform on message signal 74 | */ 75 | on(signal, fn) { 76 | this.listeners[signal] = fn 77 | } 78 | 79 | /** 80 | * Unbind a particular message signal event listener. 81 | * 82 | * @param {String} signal - The signal to unbind 83 | */ 84 | off(signal) { 85 | delete this.listeners[signal] 86 | } 87 | 88 | /** 89 | * Send a data payload to a particular signal. 90 | * 91 | * @param {String} signal - Particular action/signal such as "offersignal" 92 | * @param {[type]} data - Data payload 93 | */ 94 | send(signal, data = {}) { 95 | const message = JSON.stringify({ 96 | action: signal, 97 | data: data 98 | }) 99 | this.socket.send(message) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/providers/ws-provider.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | import Web3WSProvider from './ws-web3-provider'; 4 | import { Manager as Web3RequestManager } from 'web3-core-requestmanager'; 5 | import MiddleWare from '../middleware'; 6 | import workerTimer from '../../helpers/webWorkerTimer/index'; 7 | import { v4 as uuidv4 } from 'uuid'; 8 | import { 9 | ethSendTransaction, 10 | ethSignTransaction, 11 | ethSign, 12 | personalSign, 13 | ecRecover, 14 | ethAccounts, 15 | ethCoinbase, 16 | netVersion, 17 | decrypt, 18 | signTypedData_v3, 19 | signTypedData_v4, 20 | getEncryptionPublicKey, 21 | ethRequestAccounts 22 | } from '../methods/index'; 23 | 24 | class WSProvider { 25 | constructor(host, options, store, eventHub) { 26 | this.wsProvider = new Web3WSProvider(host, options); 27 | this.lastMessage = new Date().getTime(); 28 | const keepAlive = () => { 29 | if ( 30 | this.wsProvider.connection.readyState === 31 | this.wsProvider.connection.OPEN 32 | ) 33 | this.wsProvider.connection.send(''); 34 | if ( 35 | !Object.is(this.wsProvider, store.state.web3.currentProvider) && 36 | this.lastMessage + 10 * 60 * 1000 < new Date().getTime() //wait extra 10 minutes 37 | ) { 38 | this.wsProvider.disconnect(); 39 | workerTimer.clearInterval(this.keepAliveTimer); 40 | } 41 | }; 42 | this.keepAliveTimer = workerTimer.setInterval(keepAlive, 5000); 43 | const _this = this.wsProvider; 44 | delete this.wsProvider['send']; 45 | this.wsProvider.send = (payload, callback) => { 46 | this.lastMessage = new Date().getTime(); 47 | if (_this.connection.readyState === _this.connection.CONNECTING) { 48 | setTimeout(() => { 49 | this.wsProvider.send(payload, callback); 50 | }, 100); 51 | return; 52 | } 53 | if (_this.connection.readyState !== _this.connection.OPEN) { 54 | if (typeof _this.connection.onerror === 'function') { 55 | _this.connection.onerror(new Error('connection not open')); 56 | } 57 | callback(new Error('connection not open')); 58 | return; 59 | } 60 | const req = { 61 | payload, 62 | store, 63 | eventHub 64 | }; 65 | const middleware = new MiddleWare(); 66 | middleware.use(ethSendTransaction); 67 | middleware.use(ethSignTransaction); 68 | middleware.use(ethSign); 69 | middleware.use(personalSign); 70 | middleware.use(ecRecover); 71 | middleware.use(ethAccounts); 72 | middleware.use(ethCoinbase); 73 | middleware.use(ethRequestAccounts); 74 | middleware.use(netVersion); 75 | middleware.use(decrypt); 76 | middleware.use(signTypedData_v3); 77 | middleware.use(signTypedData_v4); 78 | middleware.use(getEncryptionPublicKey); 79 | middleware.run(req, callback).then(() => { 80 | _this.connection.send(JSON.stringify(payload)); 81 | _this._addResponseCallback(payload, callback); 82 | }); 83 | }; 84 | this.wsProvider.request = payload => { 85 | return new Promise((resolve, reject) => { 86 | this.wsProvider.send( 87 | { 88 | jsonrpc: '2.0', 89 | id: uuidv4(), 90 | method: payload.method, 91 | params: payload.params 92 | }, 93 | (err, res) => { 94 | if (err) return reject(err); 95 | else if (res.error) return reject(res.error); 96 | resolve(res.result); 97 | } 98 | ); 99 | }); 100 | }; 101 | return this.wsProvider; 102 | } 103 | } 104 | 105 | export default WSProvider; 106 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/WalletInterface.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | import { 4 | getBufferFromHex, 5 | getSignTransactionObject, 6 | sanitizeHex, 7 | calculateChainIdFromV 8 | } from './utils'; 9 | import { 10 | hashPersonalMessage, 11 | publicToAddress, 12 | bufferToHex, 13 | ecsign, 14 | isValidPrivate, 15 | isValidPublic, 16 | privateToPublic, 17 | isHexString, 18 | toBuffer as utilsToBuffer 19 | } from 'ethereumjs-utils'; 20 | import { toChecksumAddress } from './helpers/addressUtils'; 21 | 22 | const toBuffer = v => { 23 | if (isHexString(v)) { 24 | return utilsToBuffer(v); 25 | } 26 | return Buffer.from(v); 27 | }; 28 | 29 | class WalletInterface { 30 | constructor(key, isPub = false, identifier, nick, keystore) { 31 | this.nickname = nick !== null && nick !== '' ? nick : ''; 32 | this.keystore = keystore !== null && keystore !== '' ? keystore : ''; 33 | this.identifier = identifier; 34 | if (!isPub) { 35 | const _privKey = Buffer.isBuffer(key) 36 | ? key 37 | : getBufferFromHex(sanitizeHex(key)); 38 | if (!isValidPrivate(_privKey)) 39 | throw new Error( 40 | 'Private key does not satisfy the curve requirements (ie. it is invalid)' 41 | ); 42 | this.privateKey = _privKey; 43 | this.publicKey = privateToPublic(_privKey); 44 | this.isPubOnly = false; 45 | } else { 46 | const _pubKey = Buffer.isBuffer(key) ? key : getBufferFromHex(key); 47 | if (_pubKey.length !== 20 && !isValidPublic(_pubKey, true)) 48 | throw new Error('Invalid public key'); 49 | if (_pubKey.length === 20) this.isAddress = true; 50 | this.publicKey = _pubKey; 51 | this.isPubOnly = true; 52 | } 53 | } 54 | 55 | getPrivateKey() { 56 | if (this.isPubOnly) throw new Error('public key only wallet'); 57 | return this.privateKey; 58 | } 59 | 60 | getPrivateKeyString() { 61 | if (this.isPubOnly) throw new Error('public key only wallet'); 62 | return bufferToHex(this.getPrivateKey()); 63 | } 64 | 65 | getNickname() { 66 | if (this.nickname === '') return ''; 67 | return this.nickname; 68 | } 69 | 70 | getKeystore() { 71 | if (this.keystore === '') return ''; 72 | return this.keystore; 73 | } 74 | 75 | getPublicKey() { 76 | if (this.isAddress) throw new Error('Address only wallet'); 77 | return this.publicKey; 78 | } 79 | 80 | getPublicKeyString() { 81 | return bufferToHex(this.getPublicKey()); 82 | } 83 | 84 | getAddress() { 85 | if (this.isAddress) return this.publicKey; 86 | return publicToAddress(this.publicKey, true); 87 | } 88 | 89 | getAddressString() { 90 | return bufferToHex(this.getAddress()); 91 | } 92 | 93 | getChecksumAddressString() { 94 | return toChecksumAddress(this.getAddressString()); 95 | } 96 | 97 | signTransaction(txParams, signer) { 98 | if (this.isPubOnly && typeof signer !== 'function') 99 | throw new Error('public key only wallets needs a signer'); 100 | return new Promise((resolve, reject) => { 101 | signer(txParams) 102 | .then(resolve) 103 | .catch(reject); 104 | // } 105 | }); 106 | } 107 | 108 | signMessage(msg, signer) { 109 | if (this.isPubOnly && typeof signer !== 'function') 110 | throw new Error('public key only wallets needs a signer'); 111 | return new Promise((resolve, reject) => { 112 | if (!this.isPubOnly) { 113 | const msgHash = hashPersonalMessage(toBuffer(msg)); 114 | const signed = ecsign(msgHash, this.privateKey); 115 | resolve( 116 | Buffer.concat([ 117 | Buffer.from(signed.r), 118 | Buffer.from(signed.s), 119 | Buffer.from([signed.v]) 120 | ]) 121 | ); 122 | } else { 123 | signer(msg) 124 | .then(resolve) 125 | .catch(reject); 126 | } 127 | }); 128 | } 129 | } 130 | 131 | export default WalletInterface; 132 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/methods/eth_sendTransaction.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import EventNames from '../events'; 3 | import { toPayload, toError } from '../jsonrpc'; 4 | import { getSanitizedTx } from './utils'; 5 | import BigNumber from 'bignumber.js'; 6 | import debugLogger from 'debug'; 7 | const debug = debugLogger('MEWconnectWeb3'); 8 | const debugErrors = debugLogger('MEWconnectError'); 9 | 10 | const setEvents = (promiObj, tx, eventHub) => { 11 | promiObj 12 | .once('transactionHash', hash => { 13 | eventHub.emit('Hash', hash); 14 | }) 15 | .once('receipt', res => { 16 | eventHub.emit('Receipt', res); 17 | }) 18 | .on('error', err => { 19 | eventHub.emit('Error', err); 20 | }); 21 | }; 22 | export default async ({ payload, store, eventHub }, res, next) => { 23 | if (payload.method !== 'eth_sendTransaction') return next(); 24 | const tx = Object.assign({}, payload.params[0]); 25 | const localTx = Object.assign({}, tx); 26 | delete localTx['gas']; 27 | delete localTx['nonce']; 28 | try { 29 | if (!store.state.wallet) { 30 | eventHub.emit(EventNames.WALLET_NOT_CONNECTED); 31 | debug('NOT ACTIVE WALLET IDENTIFIED'); 32 | res(toError(payload.id, 'No active wallet: eth_sendTransaction', 4002)); 33 | return; 34 | } 35 | tx.nonce = !tx.nonce 36 | ? await store.state.web3.eth.getTransactionCount( 37 | store.state.wallet.getAddressString() 38 | ) 39 | : tx.nonce; 40 | if (tx.gasLimit && !tx.gas) { 41 | tx.gas = tx.gasLimit; 42 | } else if (!tx.gasLimit && tx.gas) { 43 | tx.gasLimit = tx.gas; 44 | } 45 | tx.gas = !tx.gas ? await store.state.web3.eth.estimateGas(localTx) : tx.gas; 46 | tx.gasPrice = !tx.gasPrice 47 | ? await store.state.web3.eth.getGasPrice() 48 | : tx.gasPrice; 49 | tx.chainId = !tx.chainId ? store.state.network.chainID : tx.chainId; 50 | } catch (e) { 51 | eventHub.emit(EventNames.ERROR_NOTIFY, e); 52 | debugErrors(e); 53 | res(e); 54 | return; 55 | } 56 | debug('RAW TX', tx); 57 | getSanitizedTx(tx) 58 | .then(_tx => { 59 | debug('TX', _tx); 60 | eventHub.emit(EventNames.SHOW_TX_CONFIRM_MODAL, _tx, _response => { 61 | if (_response.reject) { 62 | debug('USER DECLINED SIGN TRANSACTION & SEND'); 63 | res(toError(payload.id, 'User Rejected Request', 4001)); 64 | return; 65 | } 66 | debug('broadcasting', payload.method, _response.rawTransaction); 67 | const _promiObj = store.state.web3.eth.sendSignedTransaction( 68 | _response.rawTransaction 69 | ); 70 | _promiObj 71 | .once('transactionHash', hash => { 72 | if (store.state.wallet !== null) { 73 | if (store.noSubs) { 74 | const txHash = hash; 75 | const start = Date.now(); 76 | const interval = setInterval(() => { 77 | store.state.web3.eth 78 | .getTransactionReceipt(txHash) 79 | .then(result => { 80 | if (result !== null) { 81 | clearInterval(interval); 82 | _promiObj.emit('receipt', result); 83 | return; 84 | } 85 | const cancelInterval = 86 | (Date.now() - start) / 1000 > 60 * 60; 87 | if (cancelInterval) { 88 | clearInterval(interval); 89 | } 90 | }) 91 | .catch(err => { 92 | eventHub.emit(EventNames.ERROR_NOTIFY, err); 93 | _promiObj.emit('error', err); 94 | }); 95 | }, 5000); 96 | } 97 | } 98 | res(null, toPayload(payload.id, hash)); 99 | }) 100 | .on('error', err => { 101 | eventHub.emit(EventNames.ERROR_NOTIFY, err); 102 | debugErrors('Error: eth_sendTransaction', err); 103 | res(err); 104 | }); 105 | setEvents(_promiObj, _tx, eventHub); 106 | }); 107 | }) 108 | .catch(e => { 109 | res(e); 110 | }); 111 | }; 112 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/helpers/webWorkerTimer/index.js: -------------------------------------------------------------------------------- 1 | const createTimerObject = () => { 2 | const Timer = class { 3 | static setTimeout(cb, ms) { 4 | return setTimeout(cb, ms); 5 | } 6 | 7 | static clearTimeout(id) { 8 | return clearTimeout(id); 9 | } 10 | 11 | static setInterval(cb, ms) { 12 | return setInterval(cb, ms); 13 | } 14 | 15 | static clearInterval(id) { 16 | return clearInterval(id); 17 | } 18 | 19 | static setImmediate(cb) { 20 | if (typeof setImmediate == 'function') { 21 | return setImmediate(cb); 22 | } 23 | 24 | return setTimeout(cb); 25 | } 26 | }; 27 | return Timer; 28 | }; 29 | 30 | const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; 31 | 32 | const _timers = new Map(); 33 | 34 | const _SetTimeoutType = 0; 35 | const _SetIntervalType = 1; 36 | const _SetImmediateType = 2; 37 | const _ClearTimeoutType = 3; 38 | const _ClearIntervalType = 4; 39 | 40 | let worker; 41 | 42 | let _nextId = 0; 43 | const nextId = function() { 44 | if (_nextId == MAX_SAFE_INTEGER) { 45 | _nextId = 0; 46 | } 47 | return _nextId++; 48 | }; 49 | 50 | class Timer { 51 | static setTimeout(cb, ms) { 52 | const id = nextId(); 53 | _timers.set(id, cb); 54 | worker.postMessage({ type: _SetTimeoutType, id: id, ms: ms }); 55 | return id; 56 | } 57 | 58 | static clearTimeout(id) { 59 | _timers.delete(id); 60 | worker.postMessage({ type: _ClearTimeoutType, id: id }); 61 | } 62 | 63 | static setInterval(cb, ms) { 64 | const id = nextId(); 65 | _timers.set(id, cb); 66 | worker.postMessage({ type: _SetIntervalType, id: id, ms: ms }); 67 | return id; 68 | } 69 | 70 | static clearInterval(id) { 71 | _timers.delete(id); 72 | worker.postMessage({ type: _ClearIntervalType, id: id }); 73 | } 74 | 75 | static setImmediate(cb) { 76 | const id = nextId(); 77 | _timers.set(id, cb); 78 | worker.postMessage({ type: _SetImmediateType, id: id }); 79 | return id; 80 | } 81 | 82 | static onmessage(e) { 83 | const cb = _timers.get(e.data.id); 84 | if (cb !== undefined) { 85 | cb.call(); 86 | if (e.data.type !== _SetIntervalType) { 87 | _timers.delete(e.data.id); 88 | } 89 | } 90 | } 91 | } 92 | 93 | const workerCode = function() { 94 | return ( 95 | '(' + 96 | function() { 97 | const _wSetTimeoutType = 0; 98 | const _wSetIntervalType = 1; 99 | const _wSetImmediateType = 2; 100 | const _wClearTimeoutType = 3; 101 | const _wClearIntervalType = 4; 102 | const timers = {}; 103 | self.onmessage = e => { 104 | if (e.data.type == _wSetTimeoutType) { 105 | timers[e.data.id] = setTimeout( 106 | () => self.postMessage(e.data), 107 | e.data.ms 108 | ); 109 | } else if (e.data.type == _wSetIntervalType) { 110 | timers[e.data.id] = setInterval( 111 | () => self.postMessage(e.data), 112 | e.data.ms 113 | ); 114 | } else if (e.data.type == _wSetImmediateType) { 115 | self.postMessage(e.data); 116 | } else if (e.data.type == _wClearTimeoutType) { 117 | clearTimeout(timers[e.data.id]); 118 | delete timers[e.data.id]; 119 | } else if (e.data.type == _wClearIntervalType) { 120 | clearInterval(timers[e.data.id]); 121 | delete timers[e.data.id]; 122 | } 123 | }; 124 | }.toString() + 125 | '());' 126 | ); 127 | }; 128 | const getTimer = () => { 129 | if ( 130 | typeof navigator !== 'undefined' && 131 | (navigator.userAgent.indexOf('MSIE') !== -1 || 132 | navigator.userAgent.match(/Trident.*rv:11\./)) 133 | ) { 134 | return createTimerObject(); 135 | } else if ( 136 | typeof WorkerGlobalScope !== 'undefined' && 137 | self instanceof WorkerGlobalScope // eslint-disable-line 138 | ) { 139 | return createTimerObject(); 140 | } else if (worker === undefined) { 141 | const url = URL.createObjectURL( 142 | new Blob([workerCode()], { type: 'text/javascript' }) 143 | ); 144 | worker = new Worker(url); 145 | worker.onmessage = Timer.onmessage; 146 | return Timer; 147 | } 148 | }; 149 | export default getTimer(); 150 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@myetherwallet/mewconnect-web-client", 3 | "homepage": "https://github.com/myetherwallet/MEWconnect-web-client", 4 | "version": "2.2.0-beta.18", 5 | "main": "dist/cjs/index.js", 6 | "module": "dist/esm/index.js", 7 | "scripts": { 8 | "start": "./node_modules/.bin/vue-cli-service serve --https example/app/src/main.js", 9 | "test:jest": "npx jest --detectOpenHandles --forceExit --runInBand", 10 | "test:browser": "npx opn ./tests/browser/browser_test.html", 11 | "test:old": "npm run buildBrowserTest && npm run test:jest && npm run test:browser", 12 | "test": "npm run test:jest", 13 | "buildBrowserTest": "webpack --config tests/browser/webpack.config.js", 14 | "buildBrowserTest:watch": "webpack --config tests/browser/webpack.config.js -w", 15 | "build": "npx npm-force-resolutions && npm install && npm run lint && rimraf dist/ && npx rollup -c", 16 | "build:dev": "rimraf dist/ && npx rollup -c", 17 | "lint": "npx eslint --fix 'src/**.js'", 18 | "update:mainlist": "node src/connectProvider/fetchLists/fetchMainLists.js", 19 | "update:lists": "npm run update:mainlist && node src/connectProvider/fetchLists/index.js", 20 | "prepublishOnly": "npm install && npm run update:lists && npm run build", 21 | "prepack": "npm run build", 22 | "ci": "npm install && npm run update:lists && npm run build" 23 | }, 24 | "gitHooks": { 25 | "pre-commit": "npm run lint" 26 | }, 27 | "dependencies": { 28 | "@rollup/plugin-babel": "^5.3.0", 29 | "@rollup/plugin-commonjs": "^20.0.0", 30 | "@rollup/plugin-json": "^4.1.0", 31 | "bignumber.js": "^9.0.0", 32 | "browser-or-node": "^1.2.1", 33 | "core-js": "^3.4.4", 34 | "debug": "^4.0.1", 35 | "detect-browser": "^3.0.1", 36 | "eccrypto": "^1.1.6", 37 | "eth-sig-util": "^3.0.1", 38 | "ethereumjs-common": "^1.5.0", 39 | "ethereumjs-tx": "^2.1.2", 40 | "ethereumjs-utils": "^5.2.5", 41 | "ethjs-unit": "^0.1.6", 42 | "events": "^3.1.0", 43 | "isomorphic-ws": "^4.0.1", 44 | "logging": "^3.2.0", 45 | "mocha": "^5.2.0", 46 | "node-fetch": "^2.6.0", 47 | "platform": "^1.3.5", 48 | "promise-ws": "^1.0.0-1", 49 | "qrcode": "^1.5.0", 50 | "query-string": "^6.10.1", 51 | "secp256k1": "^3.8.0", 52 | "simple-peer": "^9.6.2", 53 | "socket.io-client": "^2.3.0", 54 | "store": "^2.0.12", 55 | "underscore": "^1.13.2", 56 | "uuid": "^3.4.0", 57 | "vue": "^2.6.10", 58 | "web3": "1.5.0", 59 | "web3-core-helpers": "1.5.0", 60 | "web3-core-method": "1.5.0", 61 | "web3-core-requestmanager": "1.5.0", 62 | "web3-utils": "1.5.0", 63 | "webrtc-adapter": "^6.4.3", 64 | "wrtc": "^0.4.6", 65 | "ws": "^7.5.3" 66 | }, 67 | "devDependencies": { 68 | "@babel/core": "^7.8.4", 69 | "@babel/plugin-external-helpers": "^7.8.3", 70 | "@babel/plugin-transform-async-to-generator": "^7.8.3", 71 | "@babel/plugin-transform-regenerator": "^7.8.3", 72 | "@babel/plugin-transform-runtime": "^7.8.3", 73 | "@babel/plugin-transform-spread": "^7.8.3", 74 | "@babel/preset-env": "^7.8.4", 75 | "@rollup/plugin-image": "^2.1.0", 76 | "@vue/cli-plugin-babel": "^4.1.0", 77 | "@vue/cli-plugin-eslint": "^4.2.3", 78 | "@vue/cli-service": "^4.1.0", 79 | "@vue/eslint-config-prettier": "^4.0.1", 80 | "axios": "^0.21.4", 81 | "babel-core": "^7.0.0-bridge.0", 82 | "babel-eslint": "^10.0.3", 83 | "babel-jest": "^25.1.0", 84 | "babel-preset-es2015-rollup": "^3.0.0", 85 | "chai": "^4.2.0", 86 | "clean-webpack-plugin": "^3.0.0", 87 | "eslint": "^6.8.0", 88 | "eslint-config-airbnb-base": "^13.2.0", 89 | "eslint-config-prettier": "^3.0.1", 90 | "eslint-plugin-import": "^2.20.1", 91 | "eslint-plugin-security": "^1.4.0", 92 | "eslint-plugin-vue": "^6.2.1", 93 | "jest": "^25.2.3", 94 | "node-sass": "^4.12.0", 95 | "nyc": "^15.0.0", 96 | "opn": "^5.5.0", 97 | "opn-cli": "^3.1.0", 98 | "regenerator-runtime": "^0.13.3", 99 | "rimraf": "^2.7.1", 100 | "rollup": "^2.56.2", 101 | "rollup-plugin-json": "^3.1.0", 102 | "sass-loader": "^8.0.0", 103 | "style-loader": "^1.1.3", 104 | "svg-inline-loader": "^0.8.0", 105 | "url-loader": "^3.0.0", 106 | "vue-template-compiler": "^2.6.10", 107 | "yorkie": "^2.0.0" 108 | }, 109 | "resolutions": { 110 | "xmlhttprequest-ssl": "1.6.1" 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/web3-provider/providers/http-provider.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 3 | import HttpRequestManger from './http-request-manager'; 4 | import MiddleWare from '../middleware'; 5 | import { 6 | ethSendTransaction, 7 | ethSignTransaction, 8 | ethSign, 9 | ethAccounts, 10 | ethCoinbase, 11 | netVersion, 12 | personalSign, 13 | ecRecover, 14 | decrypt, 15 | signTypedData_v3, 16 | signTypedData_v4, 17 | getEncryptionPublicKey, 18 | ethRequestAccounts 19 | } from '../methods/index'; 20 | import { v4 as uuidv4 } from 'uuid'; 21 | class HttpProvider { 22 | constructor(host, options, store, eventHub) { 23 | const requestManager = new HttpRequestManger(host, options); 24 | this.httpProvider = { 25 | send: (payload, callback) => { 26 | const req = { 27 | payload, 28 | store, 29 | eventHub 30 | }; 31 | const middleware = new MiddleWare(); 32 | middleware.use(ethSendTransaction); 33 | middleware.use(ethSignTransaction); 34 | middleware.use(ethSign); 35 | middleware.use(personalSign); 36 | middleware.use(ecRecover); 37 | middleware.use(ethAccounts); 38 | middleware.use(ethCoinbase); 39 | middleware.use(ethRequestAccounts); 40 | middleware.use(netVersion); 41 | middleware.use(decrypt); 42 | middleware.use(signTypedData_v3); 43 | middleware.use(signTypedData_v4); 44 | middleware.use(getEncryptionPublicKey); 45 | middleware.run(req, callback).then(() => { 46 | requestManager.provider.send(payload, callback); 47 | }); 48 | }, 49 | notificationCallbacks: [], 50 | disconnectCallbacks: [], 51 | closeCallbacks: [], 52 | accountsChangedCallbacks: [], 53 | createSubscriptions: subscription => { 54 | requestManager.addSubscription(); 55 | }, 56 | on: (type, callback) => { 57 | if (typeof callback !== 'function') 58 | throw new Error('The second parameter callback must be a function.'); 59 | 60 | switch (type) { 61 | case 'data': 62 | this.httpProvider.notificationCallbacks.push(callback); 63 | this.httpProvider.dataCallback = callback; 64 | break; 65 | case 'accountsChanged': 66 | this.httpProvider.accountsChangedCallbacks.push(callback); 67 | this.accountsChanged = callback; 68 | break; 69 | case 'disconnected': 70 | this.httpProvider.disconnectedCallback = callback; 71 | break; 72 | case 'disconnect': 73 | this.httpProvider.disconnectCallbacks.push(callback); 74 | this.httpProvider.disconnectCallback = callback; 75 | break; 76 | case 'close': 77 | this.httpProvider.closeCallbacks.push(callback); 78 | this.httpProvider.closeCallback = callback; 79 | break; 80 | } 81 | }, 82 | emit: (type, data) => { 83 | if (typeof type !== 'string') 84 | throw new Error('The first parameter type must be a string.'); 85 | 86 | switch (type) { 87 | case 'accountsChanged': 88 | this.httpProvider.accountsChangedCallbacks.forEach(function( 89 | callback 90 | ) { 91 | if (typeof callback === 'function') callback(data); 92 | }); 93 | break; 94 | case 'disconnect': 95 | this.httpProvider.disconnectCallbacks.forEach(function(callback) { 96 | if (typeof callback === 'function') callback(data); 97 | }); 98 | break; 99 | case 'close': 100 | this.httpProvider.closeCallbacks.forEach(function(callback) { 101 | if (typeof callback === 'function') callback(data); 102 | }); 103 | break; 104 | } 105 | } 106 | }; 107 | this.httpProvider.request = payload => { 108 | return new Promise((resolve, reject) => { 109 | this.httpProvider.send( 110 | { 111 | jsonrpc: '2.0', 112 | id: uuidv4(), 113 | method: payload.method, 114 | params: payload.params 115 | }, 116 | (err, res) => { 117 | if (err) return reject(err); 118 | else if (res.error) return reject(res.error); 119 | resolve(res.result); 120 | } 121 | ); 122 | }); 123 | }; 124 | return this.httpProvider; 125 | } 126 | } 127 | export default HttpProvider; 128 | -------------------------------------------------------------------------------- /src/connectWindow/images/closeIconWhite.js: -------------------------------------------------------------------------------- 1 | const icon = 2 | 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAWAAAAIACAMAAACLo0a5AAACAVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9n7yP8AAAAqnRSTlMAAQIDBAUGBwgLDA0ODxESExQVFxgZGhscHR4fICEiIyQlJicoKS0wMTIzNTc4OTs8PT5AQUJDREVGR0hJS15fYGRlZmhpam1ub3BxcnN0d3l6fICGh4iJiouMlpeYmZqbnJ6foKGipKqtrq+wsbKztLW2t7i5uru8vb6/xcbHyMnM0dLT1NfY2drb3N3f4OHi4+Tl5ufo6err7O3u8PHy8/T19vf4+fr7/H3oGy4AAAABYktHRKq+Ble+AAALB0lEQVR42u3de2NUxRnH8WcbNggWJRVvLW2kSlsvaZR201I0xaqtIWAvFKiVBmktVglgaTXB2lWLqFDl0hSCija7ZIk+r7J/hEsSk+zZc2bOPJff9w3MzIeHyWU3Z4kQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQggh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'; 3 | 4 | export default icon; 5 | -------------------------------------------------------------------------------- /test/utils/crypto-utils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import crypto from 'crypto'; 4 | import eccrypto from 'eccrypto'; 5 | import ethUtils from 'ethereumjs-utils'; 6 | import secp256k1 from 'secp256k1'; 7 | 8 | export default (() => { 9 | /** 10 | * Generate a public/private keypair using secp256k1 11 | * 12 | * @return {Object} - publicKey/privateKey object 13 | */ 14 | const generateKeys = () => { 15 | const privateKey = Buffer.from(crypto.randomBytes(32), 'hex'); 16 | const publicKey = secp256k1.publicKeyCreate(privateKey); 17 | return { 18 | publicKey, 19 | privateKey 20 | }; 21 | }; 22 | 23 | /** 24 | * Generate a connId using given a public key 25 | * 26 | * @param {String} publicKey - publicKey string (usually generated with generateKeys()) 27 | * @return {String} - connId string 28 | */ 29 | const generateConnId = publicKey => { 30 | return publicKey.toString('hex').slice(-32); 31 | }; 32 | 33 | /** 34 | * Generate a random message of 32 bytes 35 | * 36 | * @return {String} - The randomly generated string 37 | */ 38 | const generateRandomMessage = () => { 39 | return crypto.randomBytes(32).toString('hex'); 40 | }; 41 | 42 | const bufferToString = buf => { 43 | if (buf instanceof Buffer) { 44 | return buf.toString('hex'); 45 | } 46 | return buf; 47 | }; 48 | 49 | /** 50 | * Sign a message using a privateKey 51 | * 52 | * @param {String} msg - Message to sign/hash 53 | * @param {[type]} privateKey - Private key (usually generated with generateKeys()) 54 | * @return {String} - Signed message 55 | */ 56 | const signMessage = (msg, privateKey) => { 57 | const hashedMsg = ethUtils.hashPersonalMessage( 58 | ethUtils.toBuffer(bufferToString(msg)) 59 | ); 60 | const signed = ethUtils.ecsign( 61 | Buffer.from(hashedMsg), 62 | Buffer.from(privateKey, 'hex') 63 | ); 64 | const combined = Buffer.concat([ 65 | Buffer.from([signed.v]), 66 | Buffer.from(signed.r), 67 | Buffer.from(signed.s) 68 | ]); 69 | const combinedHex = combined.toString('hex'); 70 | return combinedHex; 71 | }; 72 | 73 | /** 74 | * Encrypt a set of data given a private key using eccrypto 75 | * 76 | * @param {Object/String} data - Data to encrypt 77 | * @param {String} privateKey - Private key (usually generated with generateKeys()) 78 | * @return {Object} - Encrypted data object with the following properties: 79 | * 'ciphertext', 'ephemPublicKey', 'iv', 'mac' 80 | */ 81 | const encrypt = async (data, privateKey) => { 82 | return new Promise((resolve, reject) => { 83 | const publicKey = eccrypto.getPublic(privateKey); 84 | eccrypto 85 | .encrypt(publicKey, Buffer.from(data)) 86 | .then(encryptedData => { 87 | resolve(encryptedData); 88 | }) 89 | .catch(error => { 90 | reject(error); 91 | }); 92 | }); 93 | }; 94 | 95 | /** 96 | * Decrypt an encrypted data object given a private key using eccrypto 97 | * 98 | * @param {Object} data - An encrypted data object (usually using the encrypt() function) 99 | * @param {String} privateKey - Private key (usually generated with generateKeys()) 100 | * @return {Object} - Decrypted data 101 | */ 102 | const decrypt = async (data, privateKey) => { 103 | return new Promise((resolve, reject) => { 104 | eccrypto 105 | .decrypt(privateKey, { 106 | ciphertext: Buffer.from(data.ciphertext), 107 | ephemPublicKey: Buffer.from(data.ephemPublicKey), 108 | iv: Buffer.from(data.iv), 109 | mac: Buffer.from(data.mac) 110 | }) 111 | .then(decrypted => { 112 | let result; 113 | try { 114 | if (isJSON(decrypted)) { 115 | const humanRadable = JSON.parse(decrypted); 116 | if (Array.isArray(humanRadable)) { 117 | result = humanRadable[0]; 118 | } else { 119 | result = humanRadable; 120 | } 121 | } else { 122 | result = decrypted.toString(); 123 | } 124 | } catch (e) { 125 | reject(e); 126 | } 127 | resolve(JSON.stringify(result)); 128 | }) 129 | .catch(error => { 130 | reject(error); 131 | }); 132 | }); 133 | }; 134 | 135 | const isJSON = arg => { 136 | try { 137 | JSON.parse(arg); 138 | return true; 139 | } catch (e) { 140 | return false; 141 | } 142 | }; 143 | 144 | return { 145 | generateKeys, 146 | generateConnId, 147 | generateRandomMessage, 148 | signMessage, 149 | encrypt, 150 | decrypt 151 | }; 152 | })(); 153 | -------------------------------------------------------------------------------- /src/connectClient/MewConnectCrypto.js: -------------------------------------------------------------------------------- 1 | import eccrypto from 'eccrypto'; 2 | import ethUtils from 'ethereumjs-utils'; 3 | import crypto from 'crypto'; 4 | import secp256k1 from 'secp256k1'; 5 | 6 | export default class MewConnectCrypto { 7 | static create() { 8 | return new MewConnectCrypto(); 9 | } 10 | 11 | setPrivate(pvtKey) { 12 | this.prvt = Buffer.from(pvtKey, 'hex'); 13 | this.pub = this.generatePublic(this.prvt); 14 | return { publicKey: this.pub, privateKey: this.prvt }; 15 | } 16 | 17 | generateMessage() { 18 | return crypto.randomBytes(32).toString('hex'); 19 | } 20 | 21 | // Not for the Address, but generate them for the connection check 22 | prepareKey() { 23 | this.prvt = this.generatePrivate(); 24 | this.pub = this.generatePublic(this.prvt); 25 | return { pub: this.pub, pvt: this.prvt }; 26 | } 27 | 28 | generateKeys() { 29 | this.prvt = this.generatePrivate(); 30 | this.pub = this.generatePublic(this.prvt); 31 | return { publicKey: this.pub, privateKey: this.prvt }; 32 | } 33 | 34 | generatePrivate() { 35 | let privKey; 36 | do { 37 | privKey = crypto.randomBytes(32); 38 | } while (!secp256k1.privateKeyVerify(privKey)); 39 | return privKey; 40 | } 41 | 42 | generatePublic(privKey) { 43 | const pvt = Buffer.from(privKey, 'hex'); 44 | this.prvt = pvt; 45 | return secp256k1.publicKeyCreate(pvt); 46 | } 47 | 48 | encrypt(dataToSend) { 49 | const publicKeyA = eccrypto.getPublic(this.prvt); 50 | return new Promise((resolve, reject) => { 51 | eccrypto 52 | .encrypt(publicKeyA, Buffer.from(dataToSend)) 53 | .then(_initial => { 54 | resolve(_initial); 55 | }) 56 | .catch(error => { 57 | reject(error); 58 | }); 59 | }); 60 | } 61 | 62 | decrypt(dataToSee) { 63 | return new Promise((resolve, reject) => { 64 | eccrypto 65 | .decrypt(this.prvt, { 66 | ciphertext: Buffer.from(dataToSee.ciphertext), 67 | ephemPublicKey: Buffer.from(dataToSee.ephemPublicKey), 68 | iv: Buffer.from(dataToSee.iv), 69 | mac: Buffer.from(dataToSee.mac) 70 | }) 71 | .then(_initial => { 72 | let result; 73 | try { 74 | if (this.isJSON(_initial)) { 75 | const humanRadable = JSON.parse(_initial); 76 | if (Array.isArray(humanRadable)) { 77 | result = humanRadable[0]; 78 | } else { 79 | result = humanRadable; 80 | } 81 | } else { 82 | result = _initial.toString(); 83 | } 84 | } catch (e) { 85 | console.error(e); 86 | } 87 | resolve(JSON.stringify(result)); 88 | }) 89 | .catch(error => { 90 | reject(error); 91 | }); 92 | }); 93 | } 94 | 95 | signMessage(msgToSign) { 96 | return new Promise((resolve, reject) => { 97 | try { 98 | const msg = ethUtils.hashPersonalMessage(ethUtils.toBuffer(msgToSign)); 99 | const signed = ethUtils.ecsign( 100 | Buffer.from(msg), 101 | Buffer.from(this.prvt, 'hex') 102 | ); 103 | const combined = Buffer.concat([ 104 | Buffer.from([signed.v]), 105 | Buffer.from(signed.r), 106 | Buffer.from(signed.s) 107 | ]); 108 | const combinedHex = combined.toString('hex'); 109 | resolve(combinedHex); 110 | } catch (e) { 111 | reject(e); 112 | } 113 | }); 114 | } 115 | 116 | signMessageSync(msgToSign) { 117 | msgToSign = this.bufferToString(msgToSign); 118 | 119 | const msg = ethUtils.hashPersonalMessage(ethUtils.toBuffer(msgToSign)); 120 | const signed = ethUtils.ecsign( 121 | Buffer.from(msg), 122 | Buffer.from(this.prvt, 'hex') 123 | ); 124 | const combined = Buffer.concat([ 125 | Buffer.from([signed.v]), 126 | Buffer.from(signed.r), 127 | Buffer.from(signed.s) 128 | ]); 129 | return combined.toString('hex'); 130 | } 131 | 132 | bufferToConnId(buf) { 133 | return buf.toString('hex').slice(0, 32); 134 | } 135 | 136 | generateConnId(buf) { 137 | if (buf instanceof Buffer) { 138 | return buf.toString('hex').slice(0, 32); 139 | } 140 | return Buffer.from(buf) 141 | .toString('hex') 142 | .slice(0, 32); 143 | } 144 | 145 | isJSON(arg) { 146 | try { 147 | JSON.parse(arg); 148 | return true; 149 | } catch (e) { 150 | return false; 151 | } 152 | } 153 | 154 | toBuffer(buf) { 155 | if (buf instanceof Buffer) { 156 | return buf; 157 | } 158 | return Buffer.from(buf, 'hex'); 159 | } 160 | 161 | bufferToString(buf) { 162 | if (buf instanceof Buffer) { 163 | return buf.toString('hex'); 164 | } 165 | return buf; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/connectClient/MewConnectCommon.js: -------------------------------------------------------------------------------- 1 | import EventEmitter from 'events'; 2 | import { isBrowser } from 'browser-or-node'; 3 | import { detect } from 'detect-browser'; 4 | 5 | import { 6 | versions, 7 | connectionCodeSchemas, 8 | connectionCodeSeparator, 9 | signalServer, 10 | signals, 11 | signal, 12 | rtc, 13 | iceConnectionState, 14 | stages, 15 | lifeCycle, 16 | communicationTypes 17 | } from './constants/index'; 18 | import { stunServers } from './config'; 19 | 20 | export default class MewConnectCommon extends EventEmitter { 21 | constructor(version = -1) { 22 | super(); 23 | 24 | this.isBrowser = isBrowser; 25 | 26 | this.jsonDetails = { 27 | stunSrvers: [...stunServers], 28 | signalServer: signalServer(version), 29 | signals: { 30 | ...signal(version) 31 | }, 32 | signalsV1: { 33 | ...signals.V1 34 | }, 35 | signalsV2: { 36 | ...signals.V2 37 | }, 38 | stages: { 39 | ...stages 40 | }, 41 | lifeCycle: { 42 | ...lifeCycle 43 | }, 44 | rtc: { 45 | ...rtc 46 | }, 47 | communicationTypes: { 48 | ...communicationTypes 49 | }, 50 | iceConnectionState: { 51 | ...iceConnectionState 52 | }, 53 | connectionCodeSeparator, 54 | version, 55 | versions, 56 | connectionCodeSchemas 57 | }; 58 | } 59 | 60 | isJSON(arg) { 61 | try { 62 | JSON.parse(arg); 63 | return true; 64 | } catch (e) { 65 | return false; 66 | } 67 | } 68 | 69 | static getBrowserRTC() { 70 | if (typeof window === 'undefined') return null; 71 | const wrtc = { 72 | RTCPeerConnection: 73 | // eslint-disable-next-line no-undef 74 | window.RTCPeerConnection || 75 | // eslint-disable-next-line no-undef 76 | window.mozRTCPeerConnection || 77 | // eslint-disable-next-line no-undef 78 | window.webkitRTCPeerConnection, 79 | RTCSessionDescription: 80 | // eslint-disable-next-line no-undef 81 | window.RTCSessionDescription || 82 | // eslint-disable-next-line no-undef 83 | window.mozRTCSessionDescription || 84 | // eslint-disable-next-line no-undef 85 | window.webkitRTCSessionDescription, 86 | RTCIceCandidate: 87 | // eslint-disable-next-line no-undef 88 | window.RTCIceCandidate || 89 | // eslint-disable-next-line no-undef 90 | window.mozRTCIceCandidate || 91 | // eslint-disable-next-line no-undef 92 | window.webkitRTCIceCandidate 93 | }; 94 | if (!wrtc.RTCPeerConnection) return null; 95 | return wrtc; 96 | } 97 | 98 | static checkWebRTCAvailable() { 99 | const doesNotHaveWebRTC = MewConnectCommon.getBrowserRTC() == null; 100 | return !doesNotHaveWebRTC; 101 | // return false 102 | } 103 | 104 | static checkBrowser() { 105 | let browser = detect(); 106 | if (browser === null) { 107 | browser = { version: { split: () => [1] } }; 108 | } 109 | const browserVersion = browser.version.split(0, 1)[0]; 110 | /* 111 | * Chrome > 23 112 | * Firefox > 22 113 | * Opera > 18 114 | * Safari > 11 (caveats exist) 115 | * Edge - none (RTCDataChannel not supported) 116 | * IE - none 117 | * */ 118 | if (typeof window !== 'undefined') { 119 | if (browser.name === 'safari') { 120 | // eslint-disable-next-line global-require 121 | require('webrtc-adapter'); 122 | return MewConnectCommon.buildBrowserResult( 123 | true, 124 | 'Safari', 125 | `version: ${browser.version}` 126 | ); 127 | } 128 | if (browser.name === 'ie') { 129 | return MewConnectCommon.buildBrowserResult( 130 | true, 131 | 'Internet Explorer', 132 | '', 133 | true 134 | ); 135 | } 136 | if (browser.name === 'edge') { 137 | return MewConnectCommon.buildBrowserResult( 138 | true, 139 | 'Edge', 140 | `version: ${browser.version}`, 141 | true 142 | ); 143 | } 144 | let name = ''; 145 | let minVersion = 0; 146 | 147 | if (browser.name === 'opera') { 148 | name = 'Opera'; 149 | minVersion = 18; 150 | } else if (browser.name === 'firefox') { 151 | name = 'Firefox'; 152 | minVersion = 22; 153 | } else if (browser.name === 'chrome') { 154 | name = 'Chrome'; 155 | minVersion = 23; 156 | } else { 157 | return MewConnectCommon.buildBrowserResult(false, '', '', true); 158 | } 159 | 160 | try { 161 | if (minVersion >= +browserVersion) { 162 | return MewConnectCommon.buildBrowserResult( 163 | true, 164 | name, 165 | `version: ${browserVersion}` 166 | ); 167 | } 168 | return MewConnectCommon.buildBrowserResult(false, '', ''); 169 | } catch (e) { 170 | console.error(e); 171 | } 172 | } 173 | } 174 | 175 | static buildBrowserResult(status, browser, browserVersion, noSupport) { 176 | return { 177 | status, 178 | browser, 179 | browserVersion, 180 | noSupport: noSupport || false 181 | }; 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /test/clients/initiator.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | import CryptoUtils from '@utils/crypto-utils' 4 | import WebsocketConnection from '@utils/websocket-connection' 5 | import WebRTCConnection from '@utils/webrtc-connection' 6 | import { stunServers, websocketURL } from '@config' 7 | import { signals, rtcSignals, roles } from '@signals' 8 | 9 | export default class Initiator { 10 | constructor(options = {}) { 11 | this.socket = new WebsocketConnection() 12 | this.peer = new WebRTCConnection() 13 | 14 | this.publicKey 15 | this.privateKey 16 | this.signed 17 | this.connId 18 | } 19 | 20 | /* 21 | =================================================================================== 22 | Keys 23 | =================================================================================== 24 | */ 25 | 26 | /* Set the public and private keys, connId, and signed that will be used 27 | * for the duration of the pairing process. The receiver will need to have access 28 | * to this information as well, however, the initiator creates the credentials to be 29 | * shared with the receiver. 30 | */ 31 | generateKeys() { 32 | const keys = CryptoUtils.generateKeys() 33 | this.publicKey = keys.publicKey 34 | this.privateKey = keys.privateKey 35 | this.connId = CryptoUtils.generateConnId(this.publicKey) 36 | this.signed = CryptoUtils.signMessage(this.privateKey, this.privateKey) 37 | } 38 | 39 | /* 40 | =================================================================================== 41 | Encryption 42 | =================================================================================== 43 | */ 44 | 45 | /** 46 | * Using the generated privateKey, encrypt a message. 47 | * The message must be a String, however, if an object is given, 48 | * it will become stringified for proper encryption. 49 | * 50 | * @param {Object} message - String or Object to be encrypted 51 | * @return {Object} - Encrypted message 52 | */ 53 | async encrypt(message) { 54 | message = typeof message === 'String' ? message : JSON.stringify(message) 55 | return await CryptoUtils.encrypt(message, this.privateKey) 56 | } 57 | 58 | /** 59 | * Decrypt an encrypted message using the generated privateKey. 60 | * 61 | * @param {Object} message - Message to be decrypted. 62 | * @return {Object} - Decrypted message object 63 | */ 64 | async decrypt(message) { 65 | const decryptedMessageString = await CryptoUtils.decrypt( 66 | message, 67 | this.privateKey 68 | ) 69 | return JSON.parse(decryptedMessageString) 70 | } 71 | 72 | /* 73 | =================================================================================== 74 | Websocket 75 | =================================================================================== 76 | */ 77 | 78 | /** 79 | * Given a @websocketURL, attempt to connect with given query param @options. 80 | * If no options are given, default to -what should- be the correct parameters. 81 | * 82 | * @param {String} websocketURL - WS/WSS websocket URL 83 | * @param {Object} options - (Optional) Connection query parameters 84 | */ 85 | async connect(websocketURL, options = null) { 86 | const queryOptions = options 87 | ? options 88 | : { 89 | role: roles.initiator, 90 | connId: this.connId, 91 | signed: this.signed 92 | } 93 | await this.socket.connect(websocketURL, queryOptions) 94 | } 95 | 96 | /** 97 | * Bind a particular websocket signal/event to a given callback function. 98 | * 99 | * @param {String} signal - Signal to listen to. E.g. 'onoffer' 100 | * @param {Function} fn - Callback function to perform on given signal 101 | */ 102 | on(signal, fn) { 103 | this.socket.on(signal, fn) 104 | } 105 | 106 | /** 107 | * Unbind listening to a particular signal that was bound in on() 108 | * @param {String} signal - Signal to stop listening to. E.g. 'onoffer' 109 | */ 110 | off(signal) { 111 | this.socket.off(signal) 112 | } 113 | 114 | /** 115 | * Emit a @signal event with a given @data payload. 116 | * 117 | * @param {String} signal - Signal to emit. E.g. 'offersignal' 118 | * @param {Object} data - Data/message payload to send 119 | */ 120 | send(signal, data) { 121 | this.socket.send(signal, data) 122 | } 123 | 124 | /* 125 | =================================================================================== 126 | WebRTC 127 | =================================================================================== 128 | */ 129 | 130 | /** 131 | * Attempt to create a WebRTC Offer. 132 | * Return the encrypted offer for transmission to the receiver. 133 | * 134 | * @return {Object} - Encrypted WebRTC offer 135 | */ 136 | async offer(options = null) { 137 | const offer = await this.peer.offer(options) 138 | return await this.encrypt(offer) 139 | } 140 | 141 | /** 142 | * Given a WebRTC response from the receiver, complete p2p connection. 143 | * 144 | * @param {Object} answer - WebRTC answer created by receiver 145 | */ 146 | async signal(answer) { 147 | return await this.peer.connect(answer) 148 | } 149 | 150 | /** 151 | * Disconnect from current WebRTC connection 152 | */ 153 | async disconnectRTC() { 154 | this.peer = new WebRTCConnection() 155 | } 156 | 157 | /** 158 | * On a given @signal event from the WebRTC connection, perform callback function. 159 | * 160 | * @param {String} signal - Signal to listen to. E.g. 'data' 161 | * @param {Function} fn - Callback function to perform 162 | */ 163 | onRTC(signal, fn) { 164 | this.peer.on(signal, fn) 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/connectWindow/images/mobile-icon.js: -------------------------------------------------------------------------------- 1 | const image = `data:image/png;base64, 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`; 2 | 3 | export default image; 4 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/helpers/misc.js: -------------------------------------------------------------------------------- 1 | import { isAddress } from './addressUtils'; 2 | import url from 'url'; 3 | import utils from 'web3-utils'; 4 | import { isHexString, toBuffer as utilsToBuffer } from 'ethereumjs-utils'; 5 | import { uint, address, string, bytes, bool } from './solidityTypes'; 6 | 7 | const toBuffer = v => { 8 | if (isHexString(v)) { 9 | return utilsToBuffer(v); 10 | } 11 | return Buffer.from(v); 12 | }; 13 | const capitalize = value => { 14 | if (!value) return ''; 15 | value = value.toString(); 16 | return value.charAt(0).toUpperCase() + value.slice(1); 17 | }; 18 | /* Accepts string, returns boolean */ 19 | const isJson = str => { 20 | try { 21 | JSON.parse(str); 22 | } catch (e) { 23 | return false; 24 | } 25 | 26 | return true; 27 | }; 28 | 29 | const getService = parsableUrl => { 30 | const parsedUrl = url.parse(parsableUrl).hostname; 31 | const splitUrl = parsedUrl.split('.'); 32 | if (splitUrl.length > 2) 33 | // eslint-disable-next-line 34 | return capitalize(`${splitUrl[1]}.${splitUrl[2]}`); 35 | return capitalize(splitUrl.join('.')); 36 | }; 37 | 38 | const doesExist = val => val !== undefined && val !== null; 39 | 40 | const padLeftEven = hex => { 41 | hex = hex.length % 2 !== 0 ? '0' + hex : hex; 42 | return hex; 43 | }; 44 | 45 | const isInt = num => { 46 | return num % 1 === 0; 47 | }; 48 | 49 | const formatDate = date => { 50 | const days = ['Sun', 'Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat']; 51 | const day = days[new Date(date).getDay()]; 52 | const dateString = new Date(date).toLocaleDateString(); 53 | const regExp = /\(([^)]+)\)/; 54 | const timeString = new Date(date).toTimeString(); 55 | const lengthMinus1 = timeString.length - 1; 56 | const stripTimezone = timeString 57 | .slice(timeString.indexOf('(') + 1, lengthMinus1) 58 | .split(' ') 59 | .map(item => { 60 | return item[0]; 61 | }) 62 | .join(''); 63 | const removedTimezone = timeString.replace(regExp, ''); 64 | const removeEndNumber = removedTimezone.slice(0, 12); 65 | const GMTtime = removeEndNumber.replace(removeEndNumber.slice(5, 8), ''); 66 | const localTime = new Date(date).toLocaleTimeString(navigator.language, { 67 | hour: '2-digit', 68 | minute: '2-digit' 69 | }); 70 | return `${day}. ${dateString} ${GMTtime} - ${localTime} ${stripTimezone}`; 71 | }; 72 | const isValidETHAddress = address => { 73 | return isAddress(address); 74 | }; 75 | const isValidENSorEtherAddress = address => { 76 | return isValidETHAddress(address); 77 | }; 78 | 79 | const sanitizeHex = hex => { 80 | hex = hex.substring(0, 2) == '0x' ? hex.substring(2) : hex; 81 | if (hex == '') return '0x'; 82 | return '0x' + padLeftEven(hex); 83 | }; 84 | 85 | const scrollToTop = scrollDuration => { 86 | const scrollHeight = window.scrollY, 87 | scrollStep = Math.PI / (scrollDuration / 15), 88 | cosParameter = scrollHeight / 2; 89 | 90 | let scrollCount = 0; 91 | let scrollMargin; 92 | const scrollInterval = setInterval(function() { 93 | if (window.scrollY != 0) { 94 | scrollCount = scrollCount + 1; 95 | scrollMargin = 96 | cosParameter - cosParameter * Math.cos(scrollCount * scrollStep); 97 | window.scrollTo(0, scrollHeight - scrollMargin); 98 | } else clearInterval(scrollInterval); 99 | }, 15); 100 | }; 101 | 102 | const validateHexString = str => { 103 | if (str === '') return true; 104 | str = 105 | str.substring(0, 2) === '0x' 106 | ? str.substring(2).toUpperCase() 107 | : str.toUpperCase(); 108 | return utils.isHex(str); 109 | }; 110 | 111 | const solidityType = inputType => { 112 | if (!inputType) inputType = ''; 113 | if (inputType.includes('[') && inputType.includes(']')) { 114 | if (inputType.includes(uint)) 115 | return { type: 'string', solidityType: `${uint}[]` }; 116 | if (inputType.includes(address)) 117 | return { type: 'text', solidityType: `${address}[]` }; 118 | if (inputType.includes(string)) 119 | return { type: 'text', solidityType: `${string}[]` }; 120 | if (inputType.includes(bytes)) 121 | return { type: 'text', solidityType: `${bytes}[]` }; 122 | if (inputType.includes(bool)) 123 | return { type: 'string', solidityType: `${bool}[]` }; 124 | return { type: 'text', solidityType: `${string}[]` }; 125 | } 126 | if (inputType.includes(uint)) return { type: 'number', solidityType: uint }; 127 | if (inputType.includes(address)) 128 | return { type: 'text', solidityType: address }; 129 | if (inputType.includes(string)) return { type: 'text', solidityType: string }; 130 | if (inputType.includes(bytes)) return { type: 'text', solidityType: bytes }; 131 | if (inputType.includes(bool)) return { type: 'radio', solidityType: bool }; 132 | return { type: 'text', solidityType: string }; 133 | }; 134 | 135 | const stringToArray = str => { 136 | return str.replace(/[^a-zA-Z0-9_,]+/g, '').split(','); 137 | }; 138 | 139 | const isContractArgValid = (value, solidityType) => { 140 | if (!value) value = ''; 141 | if (solidityType.includes('[') && solidityType.includes(']')) { 142 | const parsedValue = Array.isArray(value) ? value : stringToArray(value); 143 | const values = []; 144 | parsedValue.forEach(item => { 145 | if (solidityType.includes(uint)) { 146 | values.push(item !== '' && !isNaN(item) && isInt(item)); 147 | } else if (solidityType.includes(address)) { 148 | values.push(isAddress(item)); 149 | } else if (solidityType.includes(string)) { 150 | values.push(item !== ''); 151 | } else if (solidityType.includes(bool)) { 152 | values.push(typeof item === typeof true || item === ''); 153 | } else if (solidityType.includes(bytes)) { 154 | values.push(validateHexString(item)); 155 | } 156 | }); 157 | return !values.includes(false); 158 | } 159 | if (solidityType === 'uint') 160 | return value !== '' && !isNaN(value) && isInt(value); 161 | if (solidityType === 'address') return isAddress(value); 162 | if (solidityType === 'string') return true; 163 | if (solidityType === 'bytes') 164 | return value.substr(0, 2) === '0x' && validateHexString(value); 165 | if (solidityType === 'bool') 166 | return typeof value === typeof true || value === ''; 167 | return false; 168 | }; 169 | 170 | export default { 171 | isJson, 172 | doesExist, 173 | padLeftEven, 174 | formatDate, 175 | isValidENSorEtherAddress, 176 | isValidETHAddress, 177 | sanitizeHex, 178 | validateHexString, 179 | scrollToTop, 180 | solidityType, 181 | isInt, 182 | capitalize, 183 | getService, 184 | stringToArray, 185 | isContractArgValid, 186 | toBuffer 187 | }; 188 | -------------------------------------------------------------------------------- /src/connectClient/websocketWrapper.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import queryString from 'query-string'; 3 | import 'isomorphic-ws'; 4 | import debugLogger from 'debug'; 5 | 6 | const debugPeer = debugLogger('MEWconnectVerbose:websocketWrapper'); 7 | const debug = debugLogger('MEWconnect:websocketWrapper'); 8 | 9 | export default class WebsocketConnection { 10 | constructor(options = {}) { 11 | this.options = options; 12 | this.socket = {}; 13 | this.listeners = {}; 14 | 15 | this.SOCKET_STATES = { 16 | 0: 'CONNECTING', 17 | 1: 'OPEN', 18 | 2: 'CLOSING', 19 | 3: 'CLOSED' 20 | }; 21 | 22 | this.keepAlive = { 23 | ping: 'ping', 24 | pong: 'pong' 25 | }; 26 | } 27 | 28 | /** 29 | * Connect to a given @websocketURL with given @options query params. 30 | * The client will connect and bind every on "message" event to the 31 | * onMessage() function. 32 | * 33 | * In order to handle a message, the on() member function must be used to add 34 | * an listener. This gives functionality similar to socket.io, in which 35 | * it is possible to so something like: 36 | * 37 | * socket.on('error', err => {}) 38 | * as opposed to: 39 | * socket.on('message', message => { if (message.signal === 'error')... }) 40 | * 41 | * @param {String} websocketURL - WSS address of websocket API 42 | * @param {Object} options - JSON-formatted connection query params 43 | * @param {String} options.role - Either "initiator" or "receiver" accordingly 44 | * @param {String} options.connId - Last 32 characters of the public key portion of the key-pair 45 | * created for the particular paired connection 46 | * @param {String} options.signed - Private key signed with the private key created for the connection 47 | */ 48 | async connect(websocketUrl, options = {}) { 49 | try { 50 | const url = `${websocketUrl}?${queryString.stringify(options)}`; 51 | debug(url); 52 | if (typeof jest !== 'undefined' && typeof window === 'undefined') { 53 | const WebSocket = require('promise-ws').default; 54 | this.socket = await WebSocket.create(url); 55 | this.socket.on('message', this.onMessage.bind(this)); 56 | } else { 57 | this.socket = new WebSocket(url); 58 | this.socket.onmessage = this.onMessage.bind(this); 59 | this.socket.onerror = this.onError.bind(this); 60 | this.socket.onopen = this.onOpen.bind(this); 61 | this.socket.onclose = this.onClose.bind(this); 62 | 63 | debug(`extensions used: ${this.socket.extensions} or none`); 64 | debug(`protocol used: ${this.socket.protocol} or default`); 65 | debug( 66 | `binary type used: ${this.socket.binaryType} [either blob or arraybuffer]` 67 | ); 68 | } 69 | } catch (e) { 70 | debug('connect error:', e); 71 | } 72 | } 73 | 74 | async disconnect() { 75 | try { 76 | debug('ADD DISCONNECT FUNCTIONALITY'); 77 | this.socket.close(); 78 | } catch (e) { 79 | debug('disconnect error:', e); 80 | } 81 | } 82 | 83 | getSocketState() { 84 | return this.SOCKET_STATES[this.socket.readyState]; 85 | } 86 | 87 | onOpen() { 88 | debug(`websocket onopen = ${this.getSocketState()}`); 89 | // this.pinger = setInterval(() => { 90 | // this.send(this.keepAlive.ping) 91 | // }, 5000) 92 | } 93 | 94 | /** 95 | * On 'message' event, parse the message and if possible, 96 | * call the event listener with the message's particular signal. 97 | * 98 | * Messages are received as stringified JSON objects, that when parsed, 99 | * take the following format: 100 | * 101 | * { 102 | * signal, // This is the signal that the member function on() can bind to 103 | * data, // The actual data payload 104 | * message // Accompanying server message 105 | * } 106 | * 107 | * @param {String} message - Stringified JSON payload sent by the server 108 | * @return {[type]} [description] 109 | */ 110 | onMessage(message) { 111 | try { 112 | debugPeer('message', message); 113 | debugPeer('message data', message.data); 114 | let parsedMessage; 115 | if (typeof jest === 'undefined') { 116 | const parsedMessageRaw = 117 | typeof message === 'string' ? JSON.parse(message) : message; 118 | parsedMessage = 119 | typeof parsedMessageRaw.data === 'string' 120 | ? JSON.parse(parsedMessageRaw.data) 121 | : parsedMessageRaw.data; 122 | debugPeer('parsedMessage', parsedMessage); 123 | } else { 124 | parsedMessage = 125 | typeof message === 'string' ? JSON.parse(message) : message; 126 | debugPeer('parsedMessage: message', parsedMessage); 127 | debugPeer('parsedMessage: message data', parsedMessage.data); 128 | } 129 | 130 | if (parsedMessage.signal === 'ping' || parsedMessage.signal === 'pong') { 131 | return; 132 | } 133 | const signal = parsedMessage.signal; 134 | const data = parsedMessage.data; 135 | debug(`onMessage Signal: ${signal}`); 136 | try { 137 | this.listeners[signal].call(this, data); 138 | } catch (e) { 139 | debug(e); 140 | // Unhandled message signal 141 | } 142 | } catch (e) { 143 | debug('ERROR in onMessage', e); 144 | } 145 | } 146 | 147 | onError(errorEvent) { 148 | debug('Websocket ERROR'); 149 | debug('websocket error event', errorEvent); 150 | } 151 | 152 | onClose() { 153 | debug(`websocket onClose = ${this.getSocketState()}`); 154 | if (this.listeners['onClose']) { 155 | this.listeners['onClose'].call(this); 156 | } 157 | } 158 | 159 | /** 160 | * Bind an function to a particular message signal event. 161 | * E.G. 162 | * socket.on('error', err => {}) 163 | * 164 | * @param {String} signal - The signal to listen for 165 | * @param {Function} fn - Function to perform on message signal 166 | */ 167 | on(signal, fn) { 168 | this.listeners[signal] = fn; 169 | } 170 | 171 | /** 172 | * Unbind a particular message signal event listener. 173 | * 174 | * @param {String} signal - The signal to unbind 175 | */ 176 | off(signal) { 177 | delete this.listeners[signal]; 178 | } 179 | 180 | /** 181 | * Send a data payload to a particular signal. 182 | * 183 | * @param {String} signal - Particular action/signal such as "offersignal" 184 | * @param {[type]} data - Data payload 185 | */ 186 | send(signal, data = {}) { 187 | try { 188 | debug(`socket connection state: ${this.getSocketState()}`); 189 | debug(`send signal: ${signal}`); 190 | debug('send data:', data); 191 | const message = JSON.stringify({ 192 | action: signal, 193 | data: data 194 | }); 195 | this.socket.send(message); 196 | } catch (e) { 197 | debug('ERROR in send', e); 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/connectProvider/web3Provider/MEWconnect/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | import MEWconnect from '../../../index'; 3 | import { Transaction } from 'ethereumjs-tx'; 4 | import WalletInterface from '../WalletInterface'; 5 | import { 6 | getSignTransactionObject, 7 | sanitizeHex, 8 | getBufferFromHex, 9 | calculateChainIdFromV 10 | } from '../utils'; 11 | import { hashPersonalMessage } from 'ethereumjs-utils'; 12 | import errorHandler from './errorHandler'; 13 | import commonGenerator from '../helpers/commonGenerator'; 14 | import Misc from '../helpers/misc'; 15 | import debugLogger from 'debug'; 16 | 17 | // TODO add debug logging 18 | const debug = debugLogger('MEWconnect:wallet'); 19 | // const debugConnectionState = debugLogger('MEWconnect:connection-state'); 20 | 21 | const V1_SIGNAL_URL = 'https://connect.mewapi.io'; 22 | const V2_SIGNAL_URL = 'wss://connect2.mewapi.io/staging'; 23 | const IS_HARDWARE = true; 24 | 25 | class MEWconnectWalletInterface extends WalletInterface { 26 | constructor( 27 | pubkey, 28 | isHardware, 29 | identifier, 30 | txSigner, 31 | msgSigner, 32 | mewConnect, 33 | popUpHandler 34 | ) { 35 | super(pubkey, true, identifier); 36 | this.errorHandler = errorHandler(popUpHandler); 37 | this.txSigner = txSigner; 38 | this.msgSigner = msgSigner; 39 | this.isHardware = isHardware; 40 | this.mewConnect = mewConnect(); 41 | } 42 | 43 | getConnection() { 44 | return this.mewConnect; 45 | } 46 | 47 | signTransaction(txParams) { 48 | return super.signTransaction(txParams, this.txSigner); 49 | } 50 | 51 | signMessage(msg) { 52 | return super.signMessage(msg, this.msgSigner); 53 | } 54 | } 55 | 56 | class MEWconnectWallet { 57 | constructor(state, popupCreator, popUpHandler) { 58 | this.identifier = 'mew_connect'; 59 | this.isHardware = IS_HARDWARE; 60 | this.mewConnect = new MEWconnect.Initiator({ 61 | v1Url: V1_SIGNAL_URL, 62 | v2Url: V2_SIGNAL_URL, 63 | showPopup: true, 64 | popupCreator: popupCreator 65 | }); 66 | this.state = state || {}; 67 | this.popUpHandler = popUpHandler; 68 | this.txIds = []; 69 | } 70 | 71 | static setConnectionState(connectionState) { 72 | if (!connectionState) 73 | MEWconnect.Initiator.setConnectionState('disconnected'); 74 | else MEWconnect.Initiator.setConnectionState(connectionState); 75 | debug('setConnectionState', MEWconnect.Initiator.connectionState); 76 | } 77 | 78 | static getConnectionState() { 79 | debug('getConnectionState', MEWconnect.Initiator.connectionState); 80 | 81 | if (!MEWconnect.Initiator.connectionState) return 'disconnected'; 82 | return MEWconnect.Initiator.connectionState; 83 | } 84 | 85 | static getPopupWindowRef() { 86 | if (!MEWconnect.Initiator.connectionState) return 'disconnected'; 87 | return MEWconnect.Initiator.connectionState; 88 | } 89 | 90 | async init(qrcodeListener = () => {}) { 91 | this.mewConnect.on('codeDisplay', qrcodeListener); 92 | const txSigner = async tx => { 93 | let tokenInfo; 94 | if ( 95 | tx.data.slice(0, 10) === '0xa9059cbb' || 96 | tx.data.slice(0, 10) === '0x095ea7b3' 97 | ) { 98 | tokenInfo = this.state.network.tokens.find( 99 | entry => entry.address.toLowerCase() === tx.to.toLowerCase() 100 | ); 101 | if (tokenInfo) { 102 | tx.currency = { 103 | symbol: tokenInfo.symbol, 104 | decimals: tokenInfo.decimals, 105 | address: tokenInfo.address 106 | }; 107 | } 108 | } 109 | 110 | const networkId = tx.chainId; 111 | return new Promise((resolve, reject) => { 112 | if (!tx.gasLimit) { 113 | tx.gasLimit = tx.gas; 114 | } 115 | this.mewConnect.sendRtcMessage('signTx', JSON.stringify(tx)); 116 | this.mewConnect.once('signTx', result => { 117 | this.mewConnect.removeAllListeners('reject'); 118 | tx = new Transaction(sanitizeHex(result), { 119 | common: commonGenerator(this.state.network) 120 | }); 121 | const signedChainId = calculateChainIdFromV(tx.v); 122 | if (signedChainId !== networkId) 123 | throw new Error( 124 | 'Invalid networkId signature returned. Expected: ' + 125 | networkId + 126 | ', Got: ' + 127 | signedChainId, 128 | 'InvalidNetworkId' 129 | ); 130 | resolve(getSignTransactionObject(tx)); 131 | }); 132 | this.mewConnect.once('reject', () => { 133 | debug('signTx rejected'); 134 | this.mewConnect.removeAllListeners('signTx'); 135 | reject({ reject: true }); 136 | }); 137 | }); 138 | }; 139 | const msgSigner = async msg => { 140 | return new Promise((resolve, reject) => { 141 | const msgHash = hashPersonalMessage(Misc.toBuffer(msg)); 142 | this.mewConnect.sendRtcMessage('signMessage', { 143 | hash: msgHash.toString('hex'), 144 | text: msg 145 | }); 146 | this.mewConnect.once('signMessage', data => { 147 | this.mewConnect.removeAllListeners('reject'); 148 | resolve(getBufferFromHex(sanitizeHex(data.sig))); 149 | }); 150 | this.mewConnect.once('reject', () => { 151 | debug('signMessage rejected'); 152 | this.mewConnect.removeAllListeners('signMessage'); 153 | reject({ reject: true }); 154 | }); 155 | }); 156 | }; 157 | 158 | const mewConnect = () => { 159 | return this.mewConnect; 160 | }; 161 | 162 | const address = await signalerConnect(V1_SIGNAL_URL, this.mewConnect); 163 | 164 | return new MEWconnectWalletInterface( 165 | sanitizeHex(address), 166 | this.isHardware, 167 | this.identifier, 168 | txSigner, 169 | msgSigner, 170 | mewConnect, 171 | this.popUpHandler 172 | ); 173 | } 174 | } 175 | 176 | const createWallet = async (state, popupCreator, popUpHandler) => { 177 | const _MEWconnectWallet = new MEWconnectWallet( 178 | state, 179 | popupCreator, 180 | popUpHandler 181 | ); 182 | createWallet.connectionState = _MEWconnectWallet.connectionState; 183 | const _tWallet = await _MEWconnectWallet.init(); 184 | return _tWallet; 185 | }; 186 | createWallet.errorHandler = errorHandler; 187 | const signalerConnect = (url, mewConnect) => { 188 | return new Promise(resolve => { 189 | mewConnect.initiatorStart(url); 190 | mewConnect.on('RtcConnectedEvent', () => { 191 | mewConnect.sendRtcMessage('address', ''); 192 | mewConnect.once('address', data => { 193 | resolve(data.address); 194 | }); 195 | }); 196 | 197 | mewConnect.on('RtcDisconnectEvent', () => { 198 | MEWconnectWallet.setConnectionState('disconnected'); 199 | mewConnect; 200 | }); 201 | }); 202 | }; 203 | 204 | createWallet.getConnectionState = MEWconnectWallet.getConnectionState; 205 | createWallet.setConnectionState = MEWconnectWallet.setConnectionState; 206 | 207 | export default createWallet; 208 | -------------------------------------------------------------------------------- /test/clients/turnReceiver.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const debugLogger = require('debug'); 3 | import CryptoUtils from '../utils/crypto-utils' 4 | import WebsocketConnection from '../utils/websocket-connection' 5 | import WebRTCConnection from '../utils/webrtc-connection' 6 | import { stunServers, websocketURL } from '@config' 7 | import { signals, rtcSignals, roles } from '@signals' 8 | 9 | const debug = debugLogger('MEWconnect:receiver-V2'); 10 | 11 | export default class Receiver { 12 | constructor(options = {}) { 13 | this.options = options 14 | 15 | this.socket = new WebsocketConnection() 16 | // this.peer = new WebRTCConnection() 17 | this.turnTest = options.turnTest || false; 18 | 19 | this.seenOnce = false; 20 | 21 | this.publicKey 22 | this.privateKey 23 | this.signed 24 | this.connId 25 | } 26 | 27 | /* 28 | =================================================================================== 29 | Keys 30 | =================================================================================== 31 | */ 32 | 33 | /** 34 | * Set the public and private keys, connId, and signed that will be used 35 | * for the duration of the pairing process. The receiver must receiver this information 36 | * from the initiator who creates the credentials initially. 37 | * 38 | * @param {String} publicKey - Public key generated by the initiator for 39 | * secure communication during the pairing process. 40 | * @param {String} privateKey - Private key generated by the initiator for 41 | * secure communication during the pairing process. 42 | * @param {String} connId - condId generated by the initiator for secure communication 43 | * during the pairing process. 44 | */ 45 | async setKeys(publicKey, privateKey, connId) { 46 | debug(privateKey); // todo remove dev item 47 | this.publicKey = publicKey 48 | this.privateKey = privateKey 49 | this.connId = connId 50 | this.signed = CryptoUtils.signMessage(this.privateKey, this.privateKey) 51 | debug('receiver: ',this.signed); // todo remove dev item 52 | } 53 | 54 | /* 55 | =================================================================================== 56 | Encryption 57 | =================================================================================== 58 | */ 59 | 60 | /** 61 | * Using the generated privateKey, encrypt a message. 62 | * The message must be a String, however, if an object is given, 63 | * it will become stringified for proper encryption. 64 | * 65 | * @param {Object} message - String or Object to be encrypted 66 | * @return {Object} - Encrypted message 67 | */ 68 | async encrypt(message) { 69 | debug('Receiver encrypt'); // todo remove dev item 70 | 71 | message = typeof message === 'String' ? message : JSON.stringify(message) 72 | return await CryptoUtils.encrypt(message, this.privateKey) 73 | } 74 | 75 | /** 76 | * Decrypt an encrypted message using the generated privateKey. 77 | * 78 | * @param {Object} message - Message to be decrypted. 79 | * @return {Object} - Decrypted message object 80 | */ 81 | async decrypt(message) { 82 | debug('Receiver decrypt'); // todo remove dev item 83 | 84 | const decryptedMessageString = await CryptoUtils.decrypt( 85 | message, 86 | this.privateKey 87 | ) 88 | return JSON.parse(decryptedMessageString) 89 | } 90 | 91 | /* 92 | =================================================================================== 93 | Websocket 94 | =================================================================================== 95 | */ 96 | 97 | /** 98 | * Given a @websocketURL, attempt to connect with given query param @options. 99 | * If no options are given, default to -what should- be the correct parameters. 100 | * 101 | * @param {String} websocketURL - WS/WSS websocket URL 102 | * @param {Object} options - (Optional) Connection query parameters 103 | */ 104 | async connect(websocketURL, options = null) { 105 | const queryOptions = options 106 | ? options 107 | : { 108 | role: roles.receiver, 109 | connId: this.connId, 110 | signed: this.signed 111 | } 112 | debug('receiver: ', websocketURL, queryOptions); // todo remove dev item 113 | await this.socket.connect(websocketURL, queryOptions) 114 | } 115 | 116 | /** 117 | * Bind a particular websocket signal/event to a given callback function. 118 | * 119 | * @param {String} signal - Signal to listen to. E.g. 'onoffer' 120 | * @param {Function} fn - Callback function to perform on given signal 121 | */ 122 | on(signal, fn) { 123 | this.socket.on(signal, fn) 124 | } 125 | 126 | /** 127 | * Unbind listening to a particular signal that was bound in on() 128 | * @param {String} signal - Signal to stop listening to. E.g. 'onoffer' 129 | */ 130 | off(signal) { 131 | this.socket.off(signal) 132 | } 133 | 134 | /** 135 | * Emit a @signal event with a given @data payload. 136 | * 137 | * @param {String} signal - Signal to emit. E.g. 'offersignal' 138 | * @param {Object} data - Data/message payload to send 139 | */ 140 | send(signal, data) { 141 | this.socket.send(signal, data) 142 | } 143 | 144 | /* 145 | =================================================================================== 146 | WebRTC 147 | =================================================================================== 148 | */ 149 | 150 | canAnswer(){ 151 | this.seenOnce = true; 152 | } 153 | /** 154 | * Attempt to create a WebRTC answer in response to the offer created by the initiator. 155 | * Return the encrypted answer for transmission to the initiator. 156 | * 157 | * @return {Object} - Encrypted WebRTC answer 158 | */ 159 | async answer(offer) { 160 | debug("RECEIVER ANSWER", this.seenOnce, this.turnTest) 161 | if(!this.turnTest){ 162 | this.peer = new WebRTCConnection(); 163 | const answer = await this.peer.answer(offer) 164 | return await this.encrypt(answer) 165 | } else if(this.seenOnce){ 166 | this.peer = new WebRTCConnection() 167 | const answer = await this.peer.answer(offer) 168 | return await this.encrypt(answer) 169 | } 170 | 171 | 172 | } 173 | 174 | /** 175 | * Disconnect from current WebRTC connection 176 | */ 177 | async disconnectRTC() { 178 | this.peer = new WebRTCConnection() 179 | } 180 | 181 | rtcDestroy(){ 182 | this.disconnectRTC(); 183 | this.peer.destroy(); 184 | this.peer = null; 185 | } 186 | 187 | async sendRTC(signal, data = {}){ 188 | const message = JSON.stringify({ 189 | type: signal, 190 | data: data 191 | }) 192 | const encrypted = await this.encrypt(message); 193 | this.peer.peer.send(JSON.stringify(encrypted)); 194 | } 195 | 196 | /** 197 | * On a given @signal event from the WebRTC connection, perform callback function. 198 | * 199 | * @param {String} signal - Signal to listen to. E.g. 'data' 200 | * @param {Function} fn - Callback function to perform 201 | */ 202 | onRTC(signal, fn) { 203 | this.peer.on(signal, fn) 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /test/clients/receiver.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const debugLogger = require('debug'); 3 | import CryptoUtils from '../utils/crypto-utils' 4 | import WebsocketConnection from '../utils/websocket-connection' 5 | import WebRTCConnection from '../utils/webrtc-connection' 6 | import { stunServers, websocketURL } from '@config' 7 | import { signals, rtcSignals, roles } from '@signals' 8 | 9 | const debug = debugLogger('MEWconnect:receiver-V2'); 10 | 11 | export default class Receiver { 12 | constructor(options = {}) { 13 | this.options = options 14 | 15 | this.socket = new WebsocketConnection() 16 | // this.peer = new WebRTCConnection() 17 | this.turnTest = options.turnTest || false; 18 | 19 | this.seenOnce = false; 20 | 21 | this.publicKey 22 | this.privateKey 23 | this.signed 24 | this.connId 25 | } 26 | 27 | /* 28 | =================================================================================== 29 | Keys 30 | =================================================================================== 31 | */ 32 | 33 | /** 34 | * Set the public and private keys, connId, and signed that will be used 35 | * for the duration of the pairing process. The receiver must receiver this information 36 | * from the initiator who creates the credentials initially. 37 | * 38 | * @param {String} publicKey - Public key generated by the initiator for 39 | * secure communication during the pairing process. 40 | * @param {String} privateKey - Private key generated by the initiator for 41 | * secure communication during the pairing process. 42 | * @param {String} connId - condId generated by the initiator for secure communication 43 | * during the pairing process. 44 | */ 45 | async setKeys(publicKey, privateKey, connId) { 46 | debug(privateKey); // todo remove dev item 47 | this.publicKey = publicKey 48 | this.privateKey = privateKey 49 | this.connId = connId 50 | this.signed = CryptoUtils.signMessage(this.privateKey, this.privateKey) 51 | debug('receiver: ',this.signed); // todo remove dev item 52 | } 53 | 54 | /* 55 | =================================================================================== 56 | Encryption 57 | =================================================================================== 58 | */ 59 | 60 | /** 61 | * Using the generated privateKey, encrypt a message. 62 | * The message must be a String, however, if an object is given, 63 | * it will become stringified for proper encryption. 64 | * 65 | * @param {Object} message - String or Object to be encrypted 66 | * @return {Object} - Encrypted message 67 | */ 68 | async encrypt(message) { 69 | debug('Receiver encrypt'); // todo remove dev item 70 | 71 | message = typeof message === 'String' ? message : JSON.stringify(message) 72 | return await CryptoUtils.encrypt(message, this.privateKey) 73 | } 74 | 75 | /** 76 | * Decrypt an encrypted message using the generated privateKey. 77 | * 78 | * @param {Object} message - Message to be decrypted. 79 | * @return {Object} - Decrypted message object 80 | */ 81 | async decrypt(message) { 82 | debug('Receiver decrypt'); // todo remove dev item 83 | 84 | const decryptedMessageString = await CryptoUtils.decrypt( 85 | message, 86 | this.privateKey 87 | ) 88 | return JSON.parse(decryptedMessageString) 89 | } 90 | 91 | /* 92 | =================================================================================== 93 | Websocket 94 | =================================================================================== 95 | */ 96 | 97 | /** 98 | * Given a @websocketURL, attempt to connect with given query param @options. 99 | * If no options are given, default to -what should- be the correct parameters. 100 | * 101 | * @param {String} websocketURL - WS/WSS websocket URL 102 | * @param {Object} options - (Optional) Connection query parameters 103 | */ 104 | async connect(websocketURL, options = null) { 105 | const queryOptions = options 106 | ? options 107 | : { 108 | role: roles.receiver, 109 | connId: this.connId, 110 | signed: this.signed 111 | } 112 | debug('receiver: ', websocketURL, queryOptions); // todo remove dev item 113 | await this.socket.connect(websocketURL, queryOptions) 114 | } 115 | 116 | /** 117 | * Bind a particular websocket signal/event to a given callback function. 118 | * 119 | * @param {String} signal - Signal to listen to. E.g. 'onoffer' 120 | * @param {Function} fn - Callback function to perform on given signal 121 | */ 122 | on(signal, fn) { 123 | this.socket.on(signal, fn) 124 | } 125 | 126 | /** 127 | * Unbind listening to a particular signal that was bound in on() 128 | * @param {String} signal - Signal to stop listening to. E.g. 'onoffer' 129 | */ 130 | off(signal) { 131 | this.socket.off(signal) 132 | } 133 | 134 | /** 135 | * Emit a @signal event with a given @data payload. 136 | * 137 | * @param {String} signal - Signal to emit. E.g. 'offersignal' 138 | * @param {Object} data - Data/message payload to send 139 | */ 140 | send(signal, data) { 141 | this.socket.send(signal, data) 142 | } 143 | 144 | /* 145 | =================================================================================== 146 | WebRTC 147 | =================================================================================== 148 | */ 149 | 150 | canAnswer(){ 151 | this.seenOnce = true; 152 | } 153 | /** 154 | * Attempt to create a WebRTC answer in response to the offer created by the initiator. 155 | * Return the encrypted answer for transmission to the initiator. 156 | * 157 | * @return {Object} - Encrypted WebRTC answer 158 | */ 159 | async answer(offer) { 160 | debug("RECEIVER ANSWER", this.seenOnce, this.turnTest) 161 | if(!this.turnTest){ 162 | this.peer = new WebRTCConnection(); 163 | const answer = await this.peer.answer(offer) 164 | return await this.encrypt(answer) 165 | } else if(this.seenOnce){ 166 | this.seenOnce = false; 167 | this.peer = new WebRTCConnection() 168 | const answer = await this.peer.answer(offer) 169 | return await this.encrypt(answer) 170 | } 171 | 172 | 173 | } 174 | 175 | /** 176 | * Disconnect from current WebRTC connection 177 | */ 178 | async disconnectRTC() { 179 | this.peer = new WebRTCConnection() 180 | } 181 | 182 | rtcDestroy(){ 183 | this.disconnectRTC(); 184 | this.peer.destroy(); 185 | this.peer = null; 186 | } 187 | 188 | async sendRTC(signal, data = {}){ 189 | const message = JSON.stringify({ 190 | type: signal, 191 | data: data 192 | }) 193 | const encrypted = await this.encrypt(message); 194 | this.peer.peer.send(JSON.stringify(encrypted)); 195 | } 196 | 197 | /** 198 | * On a given @signal event from the WebRTC connection, perform callback function. 199 | * 200 | * @param {String} signal - Signal to listen to. E.g. 'data' 201 | * @param {Function} fn - Callback function to perform 202 | */ 203 | onRTC(signal, fn) { 204 | debug('onRTC', signal); 205 | this.peer.on(signal, fn) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/connectWindow/popUpHandler.js: -------------------------------------------------------------------------------- 1 | import { notifierCSS, connectedNotifierCSS } from './popupStyles'; 2 | import { noticeHtml, connectedNoticeHtml } from './popupHtml'; 3 | import { spaceman, closeIconBlack, closeIconWhite } from './images/index'; 4 | import { getMessage } from './messageCreator'; 5 | 6 | export default class PopUpHandler { 7 | constructor() { 8 | this.index = 0; 9 | this.checkCount = 0; 10 | this.elementId = 'mew-connect-notice-corner'; 11 | this.connectedElementId = this.elementId + '-connected'; 12 | this.initialcheckIfIdExists(); 13 | this.createNotice(); 14 | this.timeoutTracker = null; 15 | this.lastActiveElement = ''; 16 | this.connectNoticeVisible = false; 17 | } 18 | 19 | initialcheckIfIdExists() { 20 | const element = window.document.getElementById(this.elementId); 21 | if (element) { 22 | this.checkCount++; 23 | this.elementId = this.elementId + `-${this.checkCount}`; 24 | this.connectedElementId = this.elementId + '-connected'; 25 | this.initialcheckIfIdExists(); 26 | } 27 | } 28 | 29 | showNotice(text, overrides = null) { 30 | let timeoutTime = 3800; 31 | let timeoutOverride = false; 32 | if (typeof text === 'object') { 33 | text = getMessage(null, text); 34 | } else { 35 | text = getMessage(text); 36 | } 37 | if (!text) { 38 | text = 'Check your phone to continue'; 39 | } 40 | 41 | if (typeof overrides === 'number') { 42 | timeoutTime = overrides; 43 | timeoutOverride = true; 44 | } 45 | 46 | const element = window.document.getElementById(this.elementId); 47 | this.lastActiveElement = element; 48 | if (!timeoutOverride) { 49 | element.className = 'show'; 50 | 51 | const elementText = window.document.getElementById( 52 | `${this.elementId}-text` 53 | ); 54 | elementText.innerHTML = text; 55 | 56 | setTimeout(function() { 57 | element.className = element.className.replace('show', ''); 58 | }, timeoutTime); 59 | } else { 60 | element.className = 'show-in'; 61 | 62 | const elementText = window.document.getElementById( 63 | `${this.elementId}-text` 64 | ); 65 | elementText.innerHTML = text; 66 | 67 | setTimeout(function() { 68 | element.className = element.className.replace('show-in', 'show-out'); 69 | }, timeoutTime - 500); 70 | this.timeoutTracker = setTimeout(function() { 71 | element.className = element.className.replace('show-out', ''); 72 | }, timeoutTime); 73 | } 74 | } 75 | 76 | showConnectedNotice(text, overrides) { 77 | let timeoutTime = 3800; 78 | let timeoutOverride = false; 79 | 80 | if (typeof overrides === 'number') { 81 | timeoutTime = overrides; 82 | timeoutOverride = true; 83 | } 84 | const element = window.document.getElementById(this.connectedElementId); 85 | this.lastActiveElement = element; 86 | if (!timeoutOverride) { 87 | element.className = 'show'; 88 | 89 | setTimeout(function() { 90 | element.className = element.className.replace('show', ''); 91 | this.connectNoticeVisible = true; 92 | }, timeoutTime); 93 | } else { 94 | element.className = 'show-in'; 95 | 96 | setTimeout(function() { 97 | element.className = element.className.replace('show-in', 'show-out'); 98 | this.connectNoticeVisible = true; 99 | }, timeoutTime - 500); 100 | this.timeoutTracker = setTimeout(function() { 101 | element.className = element.className.replace('show-out', ''); 102 | this.connectNoticeVisible = false; 103 | this.lastActiveElement = null; 104 | }, timeoutTime); 105 | } 106 | } 107 | 108 | showNoticePersistentEnter(text) { 109 | if (typeof text === 'object') { 110 | text = getMessage(null, text); 111 | } else { 112 | text = getMessage(text); 113 | } 114 | 115 | const element = window.document.getElementById(this.elementId); 116 | 117 | element.className = 'show-persistent'; 118 | 119 | const elementText = window.document.getElementById( 120 | `${this.elementId}-text` 121 | ); 122 | elementText.innerHTML = text; 123 | 124 | this.timeoutTracker = setTimeout(function() { 125 | element.className = element.className.replace('show-persistent', ''); 126 | }, 10800); 127 | } 128 | 129 | showNoticePersistentExit() { 130 | if (this.timeoutTracker) { 131 | clearTimeout(this.timeoutTracker); 132 | const element = window.document.getElementById(this.elementId); 133 | element.className = element.className.replace( 134 | 'show-persistent', 135 | 'show-persistent-leave' 136 | ); 137 | 138 | this.timeoutTracker = setTimeout(function() { 139 | element.className = element.className.replace( 140 | 'show-persistent-leave', 141 | '' 142 | ); 143 | }, 1800); 144 | } 145 | } 146 | 147 | noShow() { 148 | if (this.timeoutTracker) { 149 | clearTimeout(this.timeoutTracker); 150 | } 151 | const element = window.document.getElementById(this.elementId); 152 | element.className = ''; 153 | } 154 | 155 | createNotice() { 156 | this.index++; 157 | 158 | const div = window.document.createElement('div'); 159 | div.id = this.elementId; 160 | div.innerHTML = noticeHtml(this.elementId, spaceman, closeIconBlack); 161 | window.document.body.appendChild(div); 162 | 163 | const css = document.createElement('style'); 164 | css.type = 'text/css'; 165 | if ('textContent' in css) css.textContent = notifierCSS(this.elementId); 166 | else css.innerText = notifierCSS(this.elementId); 167 | document.body.appendChild(css); 168 | 169 | const closeEl = document.getElementById(this.elementId + '-close'); 170 | closeEl.addEventListener('click', () => { 171 | const el = document.getElementById(this.elementId); 172 | if (this.timeoutTracker) { 173 | clearTimeout(this.timeoutTracker); 174 | } 175 | el.className = el.className.replace('show', ''); 176 | }); 177 | 178 | // create connected notice 179 | const divConn = window.document.createElement('div'); 180 | divConn.id = this.connectedElementId; 181 | divConn.innerHTML = connectedNoticeHtml( 182 | this.connectedElementId, 183 | spaceman, 184 | closeIconWhite 185 | ); 186 | window.document.body.appendChild(divConn); 187 | 188 | const cssConn = document.createElement('style'); 189 | cssConn.type = 'text/css'; 190 | if ('textContent' in cssConn) 191 | cssConn.textContent = connectedNotifierCSS(this.connectedElementId); 192 | else cssConn.innerText = connectedNotifierCSS(this.connectedElementId); 193 | document.body.appendChild(cssConn); 194 | 195 | const closeElConn = document.getElementById( 196 | this.connectedElementId + '-close' 197 | ); 198 | closeElConn.addEventListener('click', () => { 199 | const el = document.getElementById(this.connectedElementId); 200 | if (this.timeoutTracker) { 201 | clearTimeout(this.timeoutTracker); 202 | } 203 | el.className = el.className.replace('show', ''); 204 | }); 205 | } 206 | 207 | hideNotifier() { 208 | const notify = document.getElementById('Notifications'); 209 | if (notify) { 210 | notify.className = 'hidden'; 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/connectWindow/designTemplates/popupWindow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | MEWconnect 11 | 183 | 184 | 185 | 186 |
187 |
188 |

Connect to MEW wallet app

189 |

Scan this code to connect

190 |
191 | 192 |
193 | 196 | 197 |
    198 |
  1. Open MEW wallet app on your mobile device
  2. 199 |
  3. 200 | Click icon 201 | in the top right corner 202 |
  4. 203 |
  5. Scan this code to connect
  6. 204 |
205 |
206 |
207 |
208 |
209 | 215 |
216 |
217 |
218 |

Don't have MEW wallet app?

219 | 225 | 230 |
231 |
232 |
233 |
234 | Powered by MEWconnect protocol
235 | brought to you by MyEtherWallet 236 |
237 |
238 |
239 | 256 | 257 | 258 | --------------------------------------------------------------------------------