├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── index.html ├── index.mjs ├── package.json ├── public ├── background111.js ├── content-script111.js ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-48x48.png ├── favicon.ico ├── logo.png ├── logo128.png ├── logo512.png └── robots.txt ├── src ├── App.tsx ├── apis │ ├── DefaultRequest.tsx │ ├── api.tsx │ ├── get-utxos.ts │ └── push-tx.ts ├── assets │ ├── coingroup │ │ ├── Arbitrum.svg │ │ ├── NFT_Icon.png │ │ ├── USD.svg │ │ ├── arbitrum_logo.svg │ │ ├── bitcoin.svg │ │ ├── bnb.svg │ │ ├── buy.png │ │ ├── buy_active.png │ │ ├── cart.png │ │ ├── chainbits.png │ │ ├── confirmBtn.png │ │ ├── deposit.png │ │ ├── deposit_active.png │ │ ├── dogecoin.svg │ │ ├── ethereum.svg │ │ ├── litecoin.svg │ │ ├── moonpay.png │ │ ├── optimism.svg │ │ ├── paybis.png │ │ ├── payment.png │ │ ├── polygon-token.svg │ │ ├── sol.png │ │ ├── sol.svg │ │ ├── tezos.png │ │ ├── tezos.svg │ │ ├── transactions.png │ │ ├── transactions_white.png │ │ ├── tron-trx-logo.svg │ │ ├── usdc.svg │ │ ├── usdt.svg │ │ ├── withdraw.png │ │ └── withdraw_active.png │ ├── logo │ │ ├── logo128.png │ │ └── logo512.png │ └── utils │ │ ├── line-angle-down-icon.svg │ │ ├── line-angle-left-icon.svg │ │ ├── line-angle-left-white-icon.svg │ │ ├── line-angle-right-icon.svg │ │ ├── loading.gif │ │ └── loading.mp4 ├── components │ ├── Buttons │ │ ├── ButtonWithActive.tsx │ │ └── ImageButton.tsx │ ├── Icon │ │ ├── icon-eye-slash.tsx │ │ ├── icon-eye.tsx │ │ ├── index.tsx │ │ └── lock-icon │ │ │ ├── index.tsx │ │ │ └── lock-icon.component.tsx │ ├── Items │ │ └── AccountItem.tsx │ ├── Layout │ │ ├── BottomBox.tsx │ │ ├── ScrollBox.tsx │ │ └── index.tsx │ ├── Menu │ │ └── StyledMenu.tsx │ ├── Message │ │ └── index.tsx │ ├── NavBar │ │ └── index.tsx │ ├── WalletModal │ │ ├── Balances.tsx │ │ ├── BuyCrypto.tsx │ │ ├── Deposit.tsx │ │ ├── Main.tsx │ │ ├── Transactions.tsx │ │ ├── Withdraw.tsx │ │ └── index.tsx │ ├── app │ │ ├── create-new-vault │ │ │ ├── create-new-vault.scss │ │ │ ├── create-new-vault.test.ts │ │ │ ├── create-new-vault.tsx │ │ │ └── index.tsx │ │ ├── show-hide-toggle │ │ │ ├── index.scss │ │ │ ├── index.tsx │ │ │ ├── show-hide-toggle.test.ts │ │ │ └── show-hide-toggle.tsx │ │ └── srp-input │ │ │ ├── index.tsx │ │ │ ├── parse-secret-recovery-phrase.test.ts │ │ │ ├── parse-secret-recovery-phrase.ts │ │ │ ├── srp-input.scss │ │ │ ├── srp-input.test.ts │ │ │ └── srp-input.tsx │ ├── styles.ts │ └── with │ │ ├── WithDispatch.tsx │ │ ├── WithNavigate.tsx │ │ └── WithTheme.tsx ├── constants │ ├── abi │ │ ├── ERC20.abi.json │ │ └── ERC721.abi.json │ ├── address.ts │ ├── apis.ts │ ├── index.tsx │ ├── nets.ts │ ├── network.ts │ ├── routes.ts │ ├── supported-assets.ts │ └── unit.ts ├── context │ ├── AuthProvider.tsx │ ├── Query.tsx │ ├── SocketProvider.tsx │ ├── StateProvider │ │ ├── Actions │ │ │ └── BalanceAction.tsx │ │ ├── Reducers │ │ │ ├── BalanceReducer.tsx │ │ │ └── index.tsx │ │ └── index.tsx │ ├── WalletModalProvider.tsx │ └── types.tsx ├── ducks │ └── app │ │ └── app.ts ├── eth-keyring-controller-metamask.d.ts ├── eth-keyring-controller.d.ts ├── hooks │ └── useNetwork.ts ├── litecore-lib-ltc.d.ts ├── locale │ └── en │ │ └── messages.json ├── main.tsx ├── obj-multiplex.d.ts ├── pages │ ├── Account │ │ ├── account.scss │ │ ├── account.tsx │ │ ├── account_password.tsx │ │ └── index.tsx │ ├── Balances │ │ ├── balances.scss │ │ └── index.tsx │ ├── BuyCrypto │ │ ├── buycrypto.scss │ │ └── index.tsx │ ├── Deposit │ │ ├── deposit.scss │ │ └── index.tsx │ ├── Home │ │ └── index.tsx │ ├── Swap │ │ └── index.tsx │ ├── Transactions │ │ ├── index.tsx │ │ └── transactions.scss │ ├── Withdraw │ │ └── index.tsx │ ├── WithdrawNFT │ │ └── index.tsx │ ├── auth │ │ ├── auth.scss │ │ ├── forgot.tsx │ │ ├── index.tsx │ │ ├── login.tsx │ │ └── signup.tsx │ ├── error-page.tsx │ ├── first-time-flow │ │ ├── create-password │ │ │ ├── import-with-seed-phrase │ │ │ │ ├── import-with-seed-phrase.component.tsx │ │ │ │ ├── import-with-seed-phrase.container.tsx │ │ │ │ └── index.tsx │ │ │ └── new-account │ │ │ │ ├── index.tsx │ │ │ │ ├── new-account.component.tsx │ │ │ │ └── new-account.test.js │ │ ├── end-of-flow │ │ │ ├── end-of-flow.component.tsx │ │ │ ├── end-of-flow.container.tsx │ │ │ ├── end-of-flow.test.js │ │ │ ├── index.scss │ │ │ └── index.tsx │ │ ├── index.scss │ │ ├── seed-phrase │ │ │ ├── confirm-seed-phrase │ │ │ │ ├── ItemTypes.ts │ │ │ │ ├── confirm-seed-phrase.component.tsx │ │ │ │ ├── confirm-seed-phrase.container.tsx │ │ │ │ ├── draggable-seed.component.tsx │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── index.scss │ │ │ └── reveal-seed-phrase │ │ │ │ ├── __snapshots__ │ │ │ │ └── reveal-seed-phrase.test.js.snap │ │ │ │ ├── index.scss │ │ │ │ ├── index.tsx │ │ │ │ ├── reveal-seed-phrase.component.tsx │ │ │ │ ├── reveal-seed-phrase.container.tsx │ │ │ │ └── reveal-seed-phrase.test.js │ │ └── select-action │ │ │ ├── index.scss │ │ │ ├── index.tsx │ │ │ ├── select-action.component.tsx │ │ │ ├── select-action.container.tsx │ │ │ └── select-action.test.js │ └── pages.scss ├── react-app-env.d.ts ├── reportWebVitals.ts ├── scripts │ ├── background.ts │ ├── content-script.ts │ ├── controllers │ │ └── onboarding.ts │ ├── lib │ │ ├── metaRPCClientFactory.ts │ │ ├── seed-phrase-verifier.ts │ │ ├── stream-utils.ts │ │ └── util.ts │ ├── manifest.json │ ├── platforms │ │ ├── extension.test.js │ │ └── extension.ts │ ├── ui.ts │ └── wallet-controller.ts ├── selectors │ ├── first-time-flow.ts │ └── index.ts ├── setupTests.ts ├── shared │ ├── constants │ │ ├── app.ts │ │ ├── hardware-wallets.ts │ │ ├── keyrings.ts │ │ ├── network.ts │ │ ├── permissions.ts │ │ └── transaction.ts │ └── modules │ │ ├── hexstring-utils.ts │ │ ├── mv3.utils.ts │ │ └── random-id.ts ├── store │ ├── action-queue │ │ ├── index.test.js │ │ ├── index.ts │ │ └── queue.integration.test.js │ ├── actionConstants.ts │ ├── actions.ts │ ├── reducers │ │ ├── index.ts │ │ └── walletSlice.ts │ └── store.ts ├── styles │ ├── GlobalStyles.ts │ ├── css │ │ ├── design-system │ │ │ ├── breakpoints.scss │ │ │ ├── index.scss │ │ │ └── typography.scss │ │ └── index.scss │ └── theme │ │ ├── defaultTheme.ts │ │ └── styled.d.ts ├── utils │ ├── export-utils.ts │ ├── helper.tsx │ ├── i18n-helper.ts │ └── web3.ts └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.tsbuildinfo ├── vite.config.ts └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = false 12 | insert_final_newline = false 13 | end_of_line = lf -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /*.js 2 | node_modules 3 | dist -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "airbnb", 4 | "prettier/@typescript-eslint", 5 | "eslint:recommended", 6 | "plugin:react/recommended", 7 | "plugin:jsx-a11y/recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | "plugin:import/typescript", 10 | "plugin:react/jsx-runtime", 11 | "plugin:prettier/recommended", 12 | "prettier" 13 | ], 14 | "parser": "@typescript-eslint/parser", 15 | "parserOptions": { 16 | "ecmaFeatures": { 17 | "jsx": true 18 | }, 19 | "ecmaVersion": "latest", 20 | "sourceType": "module" 21 | }, 22 | "plugins": ["react", "@typescript-eslint", "import", "jsx-a11y", "react-hooks", "prettier"], 23 | "rules": { 24 | "@typescript-eslint/no-explicit-any": "off", 25 | "@typescript-eslint/no-non-null-assertion": "off", 26 | "@typescript-eslint/explicit-module-boundary-types": "off", 27 | "@typescript-eslint/no-use-before-define": "off", 28 | "@typescript-eslint/no-unused-vars": [ 29 | "warn", 30 | { 31 | "argsIgnorePattern": "^_", 32 | "varsIgnorePattern": "^_", 33 | "caughtErrorsIgnorePattern": "^_" 34 | } 35 | ], 36 | "camelcase": "off", 37 | "consistent-return": "off", 38 | "no-param-reassign": "off", 39 | "no-use-before-define": "off", 40 | "react-hooks/rules-of-hooks": "error", 41 | "react-hooks/exhaustive-deps": "warn", 42 | "react/jsx-props-no-spreading": "off", 43 | "react/prop-types": "off", 44 | "react/jsx-filename-extension": [ 45 | 1, 46 | { 47 | "extensions": [".tsx"] 48 | } 49 | ], 50 | "import/prefer-default-export": "off", 51 | "import/extensions": [ 52 | "error", 53 | "ignorePackages", 54 | { 55 | "ts": "never", 56 | "tsx": "never" 57 | } 58 | ], 59 | "prettier/prettier": [ 60 | "warn", 61 | { 62 | "endOfLine": "auto", 63 | "singleQuote": true 64 | } 65 | ] 66 | }, 67 | "settings": { 68 | "import/resolver": { 69 | "typescript": {} 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist.zip 13 | dist-ssr 14 | *.local 15 | 16 | # Editor directories and files 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 27 | 28 | #environment 29 | .env 30 | 31 | #ESLint 32 | .eslintcache 33 | 34 | # testing 35 | /coverage 36 | 37 | # production 38 | /build 39 | 40 | # misc 41 | .DS_Store 42 | .env.local 43 | .env.development.local 44 | .env.test.local 45 | .env.production.local 46 | 47 | npm-debug.log* 48 | yarn-debug.log* 49 | yarn-error.log* 50 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /*.js 2 | node_modules 3 | dist 4 | yarn.lock -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "jsxSingleQuote": true, 3 | "singleQuote": true, 4 | "semi": true, 5 | "tabWidth": 2, 6 | "trailingComma": "all", 7 | "printWidth": 100, 8 | "bracketSameLine": false, 9 | "useTabs": false, 10 | "arrowParens": "always", 11 | "endOfLine": "auto" 12 | } 13 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "esbenp.prettier-vscode", 3 | "editor.formatOnSave": true, 4 | "editor.formatOnPaste": false, 5 | // Runs Prettier, then ESLint 6 | "editor.codeActionsOnSave": ["source.formatDocument", "source.fixAll.eslint"] 7 | } 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Sasakura Hara 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/README.md -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | GameWalletExtension 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/index.mjs -------------------------------------------------------------------------------- /public/background111.js: -------------------------------------------------------------------------------- 1 | // /*global chrome*/ 2 | // console.log('started'); 3 | // let tokenData; 4 | 5 | // let connection_deposit; 6 | 7 | // function sleep(ms) { 8 | // return new Promise((resolve) => setTimeout(resolve, ms)); 9 | // } 10 | 11 | // const connect = () => { 12 | // connection_deposit?.close(); 13 | 14 | // connection_deposit = new WebSocket( 15 | // 'wss://80halgu2p0.execute-api.eu-west-1.amazonaws.com/production/', 16 | // ); 17 | 18 | // connection_deposit.onmessage = (message) => { 19 | // const json = JSON.parse(message.data); 20 | // console.log(json); 21 | // if (json?.status === 'confirmed') { 22 | // chrome.notifications.create({ 23 | // type: 'basic', 24 | // iconUrl: './favicon-32x32.png', 25 | // title: `Your ${json.type.charAt(0).toUpperCase() + json.type.slice(1)} Result`, 26 | // message: `Your ${json.type} was successfully confirmed! Please check your balance now.`, 27 | // }); 28 | // } else if (json?.status === 'not-confirmed') { 29 | // // chrome.notifications.create({ 30 | // // type: 'basic', 31 | // // iconUrl: './favicon-32x32.png', 32 | // // title: 'Your Deposit Result', 33 | // // message: 34 | // // 'Your deposit request was successful but not confirmed yet. Please wait for a while to confirm the transaction and notice your balance will be updated after that.', 35 | // // }); 36 | // } else { 37 | // chrome.notifications.create({ 38 | // type: 'basic', 39 | // iconUrl: './favicon-32x32.png', 40 | // title: `Your ${json.type.charAt(0).toUpperCase() + json.type.slice(1)} Result`, 41 | // message: `Your ${json.type} has been failed. Please check your transaction and contact us.`, 42 | // }); 43 | // } 44 | // }; 45 | 46 | // // connection_deposit.onclose = (message) => { 47 | // // connect(); 48 | // // }; 49 | // }; 50 | 51 | // chrome.runtime.onMessage.addListener(async function (request, sender, sendResponse) { 52 | // if (!connection_deposit || connection_deposit.readyState !== 1) connect(); 53 | // console.log('connection_deposit:', connection_deposit); 54 | // // await sleep(10000); 55 | // // setTimeout(() => { 56 | // sendResponse({ result: 'connect' }); 57 | // // }, 10000); 58 | // return true; 59 | // }); 60 | 61 | // chrome.runtime.onMessage.addListener((request) => { 62 | // console.log('rcvd'); 63 | // }); 64 | 65 | // chrome.windows.create( 66 | // { 67 | // url: `index.html`, 68 | // type: `popup`, 69 | // focused: true, 70 | // width: 400, 71 | // height: 600, 72 | // top: 0, 73 | // }, 74 | // () => { 75 | // console.log(`Opened popup!`); 76 | // }, 77 | // ); 78 | -------------------------------------------------------------------------------- /public/content-script111.js: -------------------------------------------------------------------------------- 1 | // import { browser } from 'webextension-polyfill-ts'; 2 | 3 | // // let count = 1; 4 | // // count++; 5 | 6 | // // document.body.style.background = '#09a33a'; 7 | 8 | // // chrome.notifications.create({ 9 | // // type: 'basic', 10 | // // iconUrl: browser.runtime.getURL('icons/cake-96.png'), 11 | // // title: 'Time for cake!', 12 | // // message: 'Something something cake', 13 | // // }); 14 | 15 | // const button = document.createElement('button'); 16 | // button.addEventListener('click', () => { 17 | // // chrome.tabs.query({ active: true, lastFocusedWindow: true }, function (tabs) { 18 | // // tabURL = tabs[0].id; 19 | // // console.log('URL from get-url.js', tabURL); 20 | // browser.runtime.sendMessage(tabURL, 'OpenPopup'); 21 | // // }); 22 | // }); 23 | 24 | // document.body.appendChild(button); 25 | 26 | // chrome.runtime.onMessage.addListener((request) => { 27 | // console.log('rcvd'); 28 | // if (request == 'OpenPopup') { 29 | // chrome.windows.create( 30 | // { 31 | // url: `index.html`, 32 | // type: `popup`, 33 | // focused: true, 34 | // width: 400, 35 | // height: 600, 36 | // top: 0, 37 | // }, 38 | // () => { 39 | // console.log(`Opened popup!`); 40 | // }, 41 | // ); 42 | // } 43 | // }); 44 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/public/favicon-48x48.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/public/favicon.ico -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/public/logo.png -------------------------------------------------------------------------------- /public/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/public/logo128.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/public/logo512.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/apis/DefaultRequest.tsx: -------------------------------------------------------------------------------- 1 | // const baseURL = 'https://4zcsu9v606.execute-api.eu-west-1.amazonaws.com'; 2 | // const baseURL = 3 | // 'https://sqju9d7m8j.execute-api.eu-west-1.amazonaws.com/default'; 4 | const baseURL = 5 | 'https://jgp4iec7b3.execute-api.eu-west-1.amazonaws.com/default'; 6 | 7 | export async function postApi(url: string, body: any) { 8 | const check_url = 9 | url.includes('https') || window.location.hostname === 'localhost' 10 | ? url 11 | : `${baseURL}${url}`; 12 | const res = await fetch(`${baseURL}${url}`, { 13 | method: 'POST', 14 | headers: { 15 | 'Content-Type': 'application/json', 16 | }, 17 | body: JSON.stringify(body), 18 | }) 19 | .then((data) => data) 20 | .catch((e) => null); 21 | 22 | return res ? res.json() : null; 23 | } 24 | 25 | export async function getApi(url: string) { 26 | const check_url = 27 | url.includes('https') || window.location.hostname === 'localhost' 28 | ? url 29 | : `${baseURL}${url}`; 30 | const res = await fetch(`${baseURL}${url}`, { 31 | method: 'GET', 32 | headers: { 33 | 'Content-Type': 'application/json', 34 | }, 35 | }) 36 | .then((data) => data) 37 | .catch((e) => null); 38 | 39 | return res ? res.json() : null; 40 | } 41 | -------------------------------------------------------------------------------- /src/apis/get-utxos.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { BLOCKCHAIR_INFO_API_KEY, BLOCKCHAIR_INFO_API_URL } from '~/constants/apis'; 3 | 4 | const bcInfoHost = BLOCKCHAIR_INFO_API_URL; 5 | const unspentUrl = (address: string, chain: string) => 6 | `${bcInfoHost}/${chain}?dashboards/address/${address}?key=${BLOCKCHAIR_INFO_API_KEY}`; 7 | 8 | // convert from blockchain.info format to bitcore-lib utxo format 9 | const convertUTXO = (utxo: any) => ({ 10 | txId: utxo.tx_hash_big_endian, 11 | vout: utxo.tx_output_n, 12 | script: utxo.script, 13 | satoshis: utxo.value, 14 | }); 15 | 16 | const getUnspentOutputs = async (address: string, chain: string) => { 17 | const url = unspentUrl(address, chain); 18 | const resp = await axios.get(url); 19 | const data = resp.data; 20 | return data.utxo; 21 | }; 22 | 23 | // raise an error if there are no spendable outputs 24 | const checkUTXOs = (utxos: any, address: string) => { 25 | utxos = utxos.filter((utxo: any) => utxo.confirmations > 0); 26 | utxos = utxos.filter((utxo: any) => utxo.value > 1000); 27 | if (utxos.length == 0) return false; 28 | return true; 29 | }; 30 | 31 | const getUTXOs = async (address: string, chain: string) => { 32 | let utxos = await getUnspentOutputs(address, chain); 33 | console.log('utxos:', utxos); 34 | if (checkUTXOs(utxos, address)) { 35 | utxos = utxos.map(convertUTXO); 36 | } else utxos = null; 37 | return utxos; 38 | }; 39 | 40 | export default getUTXOs; 41 | -------------------------------------------------------------------------------- /src/apis/push-tx.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { BLOCKCHAIR_INFO_API_KEY, BLOCKCHAIR_INFO_API_URL } from '~/constants/apis'; 3 | 4 | const bdHost = BLOCKCHAIR_INFO_API_URL; 5 | const pushTx = async (tx: string, chain: string) => { 6 | const postUrl = `${bdHost}/${chain}/push/transaction?key=${BLOCKCHAIR_INFO_API_KEY}`; 7 | const result = await axios 8 | .post(postUrl, { 9 | data: tx, 10 | }) 11 | .catch((e) => { 12 | console.log(e.response.status); 13 | return null; 14 | }); 15 | // const result = await client.post('/bitcoin/mainnet/tx/send', {body: {tx}}); 16 | // const result = await get('https://svc.blockdaemon.com/universal/v1/ethereum/mainnet/sync/block_number?apiKey=F9QZbdK9BztxGL3USOB6V7wvaGpOtRLN0pYSctZRVIOR2YUu') 17 | // .catch(e => console.log(e.response.status)) 18 | console.log(result?.status ? result.data : 'error'); 19 | return result; 20 | }; 21 | 22 | export default pushTx; 23 | -------------------------------------------------------------------------------- /src/assets/coingroup/Arbitrum.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 12 | 14 | 24 | 25 | 26 | 29 | 30 | 32 | 33 | -------------------------------------------------------------------------------- /src/assets/coingroup/NFT_Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/NFT_Icon.png -------------------------------------------------------------------------------- /src/assets/coingroup/USD.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/coingroup/bitcoin.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/coingroup/bnb.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/coingroup/buy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/buy.png -------------------------------------------------------------------------------- /src/assets/coingroup/buy_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/buy_active.png -------------------------------------------------------------------------------- /src/assets/coingroup/cart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/cart.png -------------------------------------------------------------------------------- /src/assets/coingroup/chainbits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/chainbits.png -------------------------------------------------------------------------------- /src/assets/coingroup/confirmBtn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/confirmBtn.png -------------------------------------------------------------------------------- /src/assets/coingroup/deposit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/deposit.png -------------------------------------------------------------------------------- /src/assets/coingroup/deposit_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/deposit_active.png -------------------------------------------------------------------------------- /src/assets/coingroup/dogecoin.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/coingroup/ethereum.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/coingroup/litecoin.svg: -------------------------------------------------------------------------------- 1 | litecoin-ltc -------------------------------------------------------------------------------- /src/assets/coingroup/moonpay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/moonpay.png -------------------------------------------------------------------------------- /src/assets/coingroup/optimism.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/assets/coingroup/paybis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/paybis.png -------------------------------------------------------------------------------- /src/assets/coingroup/payment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/payment.png -------------------------------------------------------------------------------- /src/assets/coingroup/polygon-token.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/coingroup/sol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/sol.png -------------------------------------------------------------------------------- /src/assets/coingroup/sol.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/coingroup/tezos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/tezos.png -------------------------------------------------------------------------------- /src/assets/coingroup/tezos.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 9 | 11 | 22 | 23 | -------------------------------------------------------------------------------- /src/assets/coingroup/transactions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/transactions.png -------------------------------------------------------------------------------- /src/assets/coingroup/transactions_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/transactions_white.png -------------------------------------------------------------------------------- /src/assets/coingroup/tron-trx-logo.svg: -------------------------------------------------------------------------------- 1 | tron -------------------------------------------------------------------------------- /src/assets/coingroup/usdc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/assets/coingroup/usdt.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/coingroup/withdraw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/withdraw.png -------------------------------------------------------------------------------- /src/assets/coingroup/withdraw_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/withdraw_active.png -------------------------------------------------------------------------------- /src/assets/logo/logo128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/logo/logo128.png -------------------------------------------------------------------------------- /src/assets/logo/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/logo/logo512.png -------------------------------------------------------------------------------- /src/assets/utils/line-angle-down-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/utils/line-angle-left-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/utils/line-angle-left-white-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/utils/line-angle-right-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/utils/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/utils/loading.gif -------------------------------------------------------------------------------- /src/assets/utils/loading.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/utils/loading.mp4 -------------------------------------------------------------------------------- /src/components/Buttons/ButtonWithActive.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, useTheme } from '@mui/material'; 3 | import { style_type_btn_active_ext, style_type_btn_ext } from '../styles'; 4 | 5 | interface VoidFn { 6 | (): void; 7 | } 8 | 9 | interface Props { 10 | isActive: boolean; 11 | label: string; 12 | size?: 'small' | 'large' | 'medium' | undefined; 13 | width?: string | number; 14 | handleFn: () => void; 15 | } 16 | 17 | const ButtonWithActive = ({ isActive, handleFn, label, size, width = 'fit-content' }: Props) => { 18 | const theme = useTheme(); 19 | 20 | return ( 21 | 38 | ); 39 | }; 40 | 41 | export default ButtonWithActive; 42 | -------------------------------------------------------------------------------- /src/components/Buttons/ImageButton.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button } from '@mui/material'; 3 | import { style_btn } from '../styles'; 4 | import LeftArrowImage from '../../assets/utils/line-angle-left-icon.svg'; 5 | import RightArrowImage from '../../assets/utils/line-angle-right-icon.svg'; 6 | 7 | interface VoidFn { 8 | (): void; 9 | } 10 | 11 | interface ButtonProps { 12 | handleClick: VoidFn; 13 | } 14 | 15 | export const PrevButtonForSwiper = () => ( 16 | 30 | ); 31 | 32 | export const NextButtonForSwiper = () => ( 33 | 47 | ); 48 | 49 | export const PrevButton = ({ handleClick }: ButtonProps) => ( 50 | 63 | ); 64 | 65 | export const NextButton = ({ handleClick }: ButtonProps) => ( 66 | 79 | ); 80 | -------------------------------------------------------------------------------- /src/components/Icon/icon-eye-slash.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const IconEyeSlash = ({ size = 24, color = 'currentColor', ariaLabel, className }: any) => ( 5 | // This SVG is copied from `@fortawesome/fontawesome-free@5.13.0/regular/eye-slash.svg`. 6 | 15 | 16 | 17 | ); 18 | 19 | IconEyeSlash.propTypes = { 20 | /** 21 | * The size of the Icon follows an 8px grid 2 = 16px, 3 = 24px etc 22 | */ 23 | size: PropTypes.number, 24 | /** 25 | * The color of the icon accepts design token css variables 26 | */ 27 | color: PropTypes.string, 28 | /** 29 | * An additional className to assign the Icon 30 | */ 31 | className: PropTypes.string, 32 | /** 33 | * The aria-label of the icon for accessibility purposes 34 | */ 35 | ariaLabel: PropTypes.string, 36 | }; 37 | 38 | export default IconEyeSlash; 39 | -------------------------------------------------------------------------------- /src/components/Icon/icon-eye.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const IconEye = ({ size = 24, color = 'currentColor', ariaLabel, className }: any) => ( 5 | // This SVG copied from `@fortawesome/fontawesome-free@5.13.0/regular/eye.svg`. 6 | 15 | 16 | 17 | ); 18 | 19 | IconEye.propTypes = { 20 | /** 21 | * The size of the Icon follows an 8px grid 2 = 16px, 3 = 24px etc 22 | */ 23 | size: PropTypes.number, 24 | /** 25 | * The color of the icon accepts design token css variables 26 | */ 27 | color: PropTypes.string, 28 | /** 29 | * An additional className to assign the Icon 30 | */ 31 | className: PropTypes.string, 32 | /** 33 | * The aria-label of the icon for accessibility purposes 34 | */ 35 | ariaLabel: PropTypes.string, 36 | }; 37 | 38 | export default IconEye; 39 | -------------------------------------------------------------------------------- /src/components/Icon/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Icon = (icon: any, width?: number) => 4 | icon ? ( 5 | icon 11 | ) : null; 12 | 13 | export default Icon; 14 | 15 | export const DownIcon = (icon: any, width?: number) => 16 | icon ? ( 17 | icon 29 | ) : null; 30 | -------------------------------------------------------------------------------- /src/components/Icon/lock-icon/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './lock-icon.component'; 2 | -------------------------------------------------------------------------------- /src/components/Icon/lock-icon/lock-icon.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function LockIcon(props: any) { 4 | return ( 5 | 19 | 20 | 29 | 30 | 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /src/components/Items/AccountItem.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, Box, Typography } from '@mui/material'; 3 | import ContentCopyIcon from '@mui/icons-material/ContentCopy'; 4 | import { style_btn_copy } from 'src/components/styles'; 5 | import { useTheme } from '@mui/material'; 6 | 7 | const AccountItem = ({ 8 | label, 9 | value, 10 | copyFn, 11 | }: { 12 | label: string; 13 | value: string; 14 | copyFn: () => void; 15 | }) => { 16 | const theme = useTheme(); 17 | return ( 18 | <> 19 |
20 | 21 | 27 | {label} 28 | 29 | 30 | 43 | {value} 44 | 45 | 48 | 49 | 50 | 51 | ); 52 | }; 53 | 54 | export default AccountItem; 55 | -------------------------------------------------------------------------------- /src/components/Layout/BottomBox.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Box } from '@mui/material'; 3 | 4 | type ScrollBoxProps = { 5 | height?: number; 6 | children: boolean | React.ReactElement | (React.ReactElement | boolean)[]; 7 | }; 8 | const BottomBox = ({ height = 420, children }: ScrollBoxProps) => { 9 | return ( 10 | 20 | {children} 21 | 22 | ); 23 | }; 24 | 25 | export default BottomBox; 26 | -------------------------------------------------------------------------------- /src/components/Layout/ScrollBox.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | type ScrollBoxProps = { 4 | height?: number; 5 | children: boolean | React.ReactElement | (React.ReactElement | boolean)[]; 6 | }; 7 | const ScrollBox = ({ height = 420, children }: ScrollBoxProps) => { 8 | return ( 9 |
19 | {children} 20 |
21 | ); 22 | }; 23 | 24 | export default ScrollBox; 25 | -------------------------------------------------------------------------------- /src/components/Layout/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, Box } from '@mui/material'; 3 | import { useAuth } from '~/context/AuthProvider'; 4 | import { useNavigate } from 'react-router-dom'; 5 | import NavBar from '../NavBar'; 6 | import { AuthState } from '~/constants'; 7 | import { useSocket } from '~/context/SocketProvider'; 8 | import { Rings } from 'react-loading-icons'; 9 | import LoadingIcon from 'src/assets/utils/loading.gif'; 10 | 11 | interface LayoutProps { 12 | children: React.ReactElement; 13 | } 14 | 15 | const Layout = ({ children }: LayoutProps) => { 16 | const navigate = useNavigate(); 17 | const { authed } = useAuth(); 18 | const { loading, networkError } = useSocket(); 19 | // if (authed !== AuthState.AUTHED) { 20 | // return
{children}
; 21 | // } else { 22 | return ( 23 | 24 | {networkError ? ( 25 | Network Error... 26 | ) : ( 27 | // ) : loading ? ( 28 | // 29 | // 30 | <> 31 | 32 | {children} 33 | 34 | )} 35 | 36 | ); 37 | // } 38 | }; 39 | 40 | export default Layout; 41 | -------------------------------------------------------------------------------- /src/components/Menu/StyledMenu.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { styled, alpha } from '@mui/material/styles'; 3 | import Menu, { MenuProps } from '@mui/material/Menu'; 4 | 5 | const StyledMenu: any = styled((props: MenuProps) => ( 6 | 18 | ))(({ theme }) => ({ 19 | '& .MuiPaper-root': { 20 | backgroundColor: alpha('#555555', 1) + ' !important', 21 | borderRadius: 20, 22 | minWidth: 160, 23 | color: theme.palette.mode === 'light' ? 'white' : 'white', 24 | marginTop: '-20px', 25 | marginLeft: '-10px', 26 | padding: 0, 27 | boxShadow: 28 | 'rgb(255, 255, 255) 0px 0px 0px 0px, rgba(0, 0, 0, 0.05) 0px 0px 0px 1px, rgba(0, 0, 0, 0.1) 0px 10px 15px -3px, rgba(0, 0, 0, 0.05) 0px 4px 6px -2px', 29 | '& .MuiMenu-list': { 30 | padding: '20px 0', 31 | }, 32 | '& .MuiMenuItem-root': { 33 | backgroundColor: alpha('#555555', 1), 34 | '& .MuiSvgIcon-root': { 35 | fontSize: 24, 36 | color: theme.palette.text.secondary, 37 | marginRight: theme.spacing(1.5), 38 | }, 39 | '&:active, &:hover': { 40 | backgroundColor: alpha('#777777', 1) + ' !important', 41 | }, 42 | }, 43 | }, 44 | })); 45 | 46 | export default StyledMenu; 47 | -------------------------------------------------------------------------------- /src/components/Message/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, Typography, useTheme } from '@mui/material'; 3 | import { style_type_btn_active_ext, style_type_btn_ext } from '../styles'; 4 | 5 | interface Props { 6 | type: string; 7 | msg: string; 8 | } 9 | 10 | const Message = ({ type = 'error', msg }: Props) => { 11 | const theme = useTheme(); 12 | 13 | return ( 14 | 24 | {msg} 25 | 26 | ); 27 | }; 28 | 29 | export default Message; 30 | -------------------------------------------------------------------------------- /src/components/WalletModal/BuyCrypto.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Button, Box, Typography } from '@mui/material'; 3 | 4 | import MoonpayIcon from '../../assets/coingroup/moonpay.png'; 5 | import ChainbitsIcon from '../../assets/coingroup/chainbits.png'; 6 | import PaybisIcon from '../../assets/coingroup/paybis.png'; 7 | 8 | const style_btn = { 9 | backgroundColor: 'white', 10 | color: '#F2F2F288', 11 | fontSize: '14px', 12 | borderRadius: '10px', 13 | padding: 0, 14 | height: 'fit-content', 15 | lineHeight: 0, 16 | }; 17 | 18 | const style_btn_active = { 19 | backgroundColor: 'white', 20 | color: '#F2F2F288', 21 | fontSize: '14px', 22 | fontWeight: 'bold', 23 | borderRadius: '10px', 24 | padding: 0, 25 | height: 'fit-content', 26 | lineHeight: 0, 27 | }; 28 | 29 | const Icon = (icon: any) => ( 30 | BTCIcon 38 | ); 39 | 40 | const payments = [ 41 | { 42 | name: 'MoonPay', 43 | icon: MoonpayIcon, 44 | url: 'https://www.moonpay.com/', 45 | }, 46 | { 47 | name: 'CHAINBITS', 48 | icon: ChainbitsIcon, 49 | url: 'https://www.chainbits.com/', 50 | }, 51 | { 52 | name: 'paybis', 53 | icon: PaybisIcon, 54 | url: 'https://paybis.com/', 55 | }, 56 | ]; 57 | 58 | const BuyCrypto = ({ handleClose }: any) => { 59 | const [activePaymentIndex, setActivePaymentIndex] = useState(0); 60 | 61 | const handleTokenChange = (index: number) => { 62 | if (index !== activePaymentIndex) { 63 | setActivePaymentIndex(index); 64 | } 65 | }; 66 | 67 | return ( 68 | 69 | 70 | BUY CRYPTO USING CARDS 71 | 72 | 81 | You can purchase crypto using your card via the recommended third-party services below. 82 | After purchasing the crypto, you can deposit it directly to your wallet account. 83 | 84 |
85 | {payments?.map((payment, index) => ( 86 | 101 | ))} 102 |
103 | {/* 104 | Duis mollis, est non commodo luctus, nisi erat porttitor ligula. 105 | */} 106 |
107 | ); 108 | }; 109 | 110 | export default BuyCrypto; 111 | -------------------------------------------------------------------------------- /src/components/WalletModal/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-unescaped-entities */ 2 | import React, { useState } from 'react'; 3 | import { Box, Modal } from '@mui/material'; 4 | import CloseIcon from '@mui/icons-material/Close'; 5 | import { ToastContainer, toast, ToastPosition, Theme } from 'react-toastify'; 6 | // import BuyCrypto from './BuyCrypto'; 7 | import Transactions from './Transactions'; 8 | import { useWalletModal } from '../../context/WalletModalProvider'; 9 | import Main from './Main'; 10 | 11 | const style_modal = { 12 | position: 'absolute', 13 | top: '50%', 14 | left: '50%', 15 | transform: 'translate(-50%, -50%)', 16 | width: '820px', 17 | height: 'fit-content', 18 | padding: 0, 19 | backgroundColor: '#17181b', 20 | borderRadius: '20px', 21 | }; 22 | 23 | interface VoidPureFuntion { 24 | (): void; 25 | } 26 | 27 | const WalletModal = () => { 28 | const [isTransactionOpen, setIsTransactionOpen] = useState(false); 29 | 30 | const { open, setOpen, setModalType } = useWalletModal(); 31 | 32 | const transactionsOpen: VoidPureFuntion = () => setIsTransactionOpen(true); 33 | const transactionsClose: VoidPureFuntion = () => setIsTransactionOpen(false); 34 | 35 | const handleClose: VoidPureFuntion = () => { 36 | setOpen(false); 37 | transactionsClose(); 38 | setModalType(0); 39 | }; 40 | 41 | return ( 42 | 48 | 49 | {!isTransactionOpen ? ( 50 |
51 | ) : ( 52 | 53 | )} 54 | 55 | 56 | ); 57 | }; 58 | 59 | export default WalletModal; 60 | -------------------------------------------------------------------------------- /src/components/app/create-new-vault/create-new-vault.scss: -------------------------------------------------------------------------------- 1 | .create-new-vault { 2 | &__form { 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | &__create-password { 8 | display: flex; 9 | flex-direction: column; 10 | width: 360px; 11 | } 12 | 13 | &__terms { 14 | margin-top: 16px; 15 | margin-bottom: 16px; 16 | } 17 | 18 | &__terms-label { 19 | margin-left: 8px; 20 | } 21 | 22 | &__terms-link { 23 | color: var(--color-primary-default); 24 | } 25 | 26 | &__submit-button#{&}__submit-button { 27 | margin-top: 16px; 28 | width: 170px; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/components/app/create-new-vault/create-new-vault.test.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // import { screen } from '@testing-library/react'; 3 | // import { renderWithProvider } from '../../../../test/jest'; 4 | // import configureStore from '../../../store/store'; 5 | // import mockState from '../../../../test/data/mock-state.json'; 6 | // import CreateNewVault from './create-new-vault'; 7 | 8 | // const store = configureStore({ 9 | // metamask: { 10 | // ...mockState.metamask, 11 | // }, 12 | // }); 13 | 14 | // describe('CreateNewVault', () => { 15 | // it('renders CreateNewVault component and shows Secret Recovery Phrase text', () => { 16 | // renderWithProvider(, store); 17 | // expect(screen.getByText('Secret Recovery Phrase')).toBeInTheDocument(); 18 | // }); 19 | 20 | // it('renders CreateNewVault component and shows You can paste... text', () => { 21 | // renderWithProvider( 22 | // , 23 | // store, 24 | // ); 25 | // expect( 26 | // screen.getByText( 27 | // 'You can paste your entire secret recovery phrase into any field', 28 | // ), 29 | // ).toBeInTheDocument(); 30 | // }); 31 | // }); 32 | -------------------------------------------------------------------------------- /src/components/app/create-new-vault/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './create-new-vault'; 2 | -------------------------------------------------------------------------------- /src/components/app/show-hide-toggle/index.scss: -------------------------------------------------------------------------------- 1 | .show-hide-toggle { 2 | position: relative; 3 | display: inline-flex; 4 | 5 | &__input { 6 | appearance: none; 7 | 8 | + .show-hide-toggle__label { 9 | cursor: pointer; 10 | user-select: none; 11 | } 12 | 13 | /* Focused when tabbing with keyboard */ 14 | &:focus, 15 | &:focus-visible { 16 | outline: none; 17 | 18 | + .show-hide-toggle__label { 19 | outline: Highlight auto 1px; 20 | } 21 | } 22 | 23 | &:disabled { 24 | + label { 25 | opacity: 0.5; 26 | cursor: auto; 27 | } 28 | } 29 | } 30 | 31 | &__icon { 32 | color: var(--color-icon-default); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/components/app/show-hide-toggle/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './show-hide-toggle'; 2 | -------------------------------------------------------------------------------- /src/components/app/show-hide-toggle/show-hide-toggle.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import classnames from 'classnames'; 4 | 5 | import IconEye from '../../Icon/icon-eye'; 6 | import IconEyeSlash from '../../Icon/icon-eye-slash'; 7 | 8 | const ShowHideToggle = ({ 9 | id, 10 | shown, 11 | onChange, 12 | ariaLabelHidden, 13 | ariaLabelShown, 14 | className, 15 | 'data-testid': dataTestId, 16 | disabled, 17 | title, 18 | }: any) => { 19 | return ( 20 |
21 | 30 | 37 |
38 | ); 39 | }; 40 | 41 | ShowHideToggle.propTypes = { 42 | /** 43 | * The id of the ShowHideToggle for htmlFor 44 | */ 45 | id: PropTypes.string.isRequired, 46 | /** 47 | * If the ShowHideToggle is in the "shown" state or not 48 | */ 49 | shown: PropTypes.bool.isRequired, 50 | /** 51 | * The onChange handler of the ShowHideToggle 52 | */ 53 | onChange: PropTypes.func.isRequired, 54 | /** 55 | * The aria-label of the icon representing the "hidden" state 56 | */ 57 | ariaLabelHidden: PropTypes.string.isRequired, 58 | /** 59 | * The aria-label of the icon representing the "shown" state 60 | */ 61 | ariaLabelShown: PropTypes.string.isRequired, 62 | /** 63 | * An additional className to give the ShowHideToggle 64 | */ 65 | className: PropTypes.string, 66 | /** 67 | * The data test id of the input 68 | */ 69 | 'data-testid': PropTypes.string, 70 | /** 71 | * Whether the input is disabled or not 72 | */ 73 | disabled: PropTypes.bool, 74 | /** 75 | * The title for the toggle. This is shown in a tooltip on hover. 76 | */ 77 | title: PropTypes.string, 78 | }; 79 | 80 | export default ShowHideToggle; 81 | -------------------------------------------------------------------------------- /src/components/app/srp-input/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './srp-input'; 2 | -------------------------------------------------------------------------------- /src/components/app/srp-input/parse-secret-recovery-phrase.test.ts: -------------------------------------------------------------------------------- 1 | import { parseSecretRecoveryPhrase } from './parse-secret-recovery-phrase'; 2 | 3 | describe('parseSecretRecoveryPhrase', () => { 4 | it('should handle a regular Secret Recovery Phrase', () => { 5 | expect(parseSecretRecoveryPhrase('foo bar baz')).toStrictEqual( 6 | 'foo bar baz', 7 | ); 8 | }); 9 | 10 | it('should handle a mixed-case Secret Recovery Phrase', () => { 11 | expect(parseSecretRecoveryPhrase('FOO bAr baZ')).toStrictEqual( 12 | 'foo bar baz', 13 | ); 14 | }); 15 | 16 | it('should handle an upper-case Secret Recovery Phrase', () => { 17 | expect(parseSecretRecoveryPhrase('FOO BAR BAZ')).toStrictEqual( 18 | 'foo bar baz', 19 | ); 20 | }); 21 | 22 | it('should trim extraneous whitespace from the given Secret Recovery Phrase', () => { 23 | expect(parseSecretRecoveryPhrase(' foo bar baz ')).toStrictEqual( 24 | 'foo bar baz', 25 | ); 26 | }); 27 | 28 | it('should return an empty string when given a whitespace-only string', () => { 29 | expect(parseSecretRecoveryPhrase(' ')).toStrictEqual(''); 30 | }); 31 | 32 | it('should return an empty string when given a string with only symbols', () => { 33 | expect(parseSecretRecoveryPhrase('$')).toStrictEqual(''); 34 | }); 35 | 36 | it('should return an empty string for both null and undefined', () => { 37 | expect(parseSecretRecoveryPhrase(undefined)).toStrictEqual(''); 38 | expect(parseSecretRecoveryPhrase(null)).toStrictEqual(''); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/components/app/srp-input/parse-secret-recovery-phrase.ts: -------------------------------------------------------------------------------- 1 | export const parseSecretRecoveryPhrase = (seedPhrase: any) => 2 | (seedPhrase || '').trim().toLowerCase().match(/\w+/gu)?.join(' ') || ''; 3 | -------------------------------------------------------------------------------- /src/components/app/srp-input/srp-input.scss: -------------------------------------------------------------------------------- 1 | .import-srp { 2 | &__container { 3 | display: grid; 4 | grid-template-areas: 5 | 'title dropdown' 6 | 'paste-tip paste-tip' 7 | 'input input' 8 | 'error error' 9 | 'too-many-words-error too-many-words-error'; 10 | } 11 | 12 | @include screen-md-max { 13 | &__container { 14 | grid-template-areas: 15 | 'title' 16 | 'dropdown' 17 | 'paste-tip' 18 | 'input' 19 | 'error' 20 | 'too-many-words-error'; 21 | } 22 | } 23 | 24 | &__srp-label { 25 | grid-area: title; 26 | text-align: center; 27 | } 28 | 29 | &__number-of-words-dropdown { 30 | grid-area: dropdown; 31 | } 32 | 33 | &__paste-tip { 34 | margin: 24px 0; 35 | grid-area: paste-tip; 36 | width: auto; 37 | margin-left: auto; 38 | margin-right: auto; 39 | } 40 | 41 | &__srp { 42 | display: grid; 43 | grid-template-columns: 1fr 1fr 1fr; 44 | grid-area: input; 45 | } 46 | 47 | @include screen-md-max { 48 | &__srp { 49 | grid-template-columns: 1fr; 50 | } 51 | } 52 | 53 | &__srp-word { 54 | display: flex; 55 | align-items: center; 56 | margin: 8px; 57 | } 58 | 59 | &__srp-word-label { 60 | width: 2em; 61 | } 62 | 63 | &__srp-error { 64 | margin-top: 4px; 65 | grid-area: error; 66 | } 67 | 68 | &__srp-too-many-words-error { 69 | margin-top: 4px; 70 | grid-area: too-many-words-error; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/components/styles.ts: -------------------------------------------------------------------------------- 1 | import styled from 'styled-components'; 2 | import { grey } from '@mui/material/colors'; 3 | 4 | export const Container = styled.div` 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: center; 8 | align-items: center; 9 | height: 100vh; 10 | 11 | > svg { 12 | margin: 40px; 13 | transition: all 0.2s ease; 14 | 15 | &:hover { 16 | color: tomato; 17 | } 18 | } 19 | `; 20 | 21 | export const Section = styled.div` 22 | display: flex; 23 | flex-direction: column; 24 | align-items: center; 25 | `; 26 | 27 | export const Description = styled.h2``; 28 | 29 | export const Warning = styled.h4``; 30 | 31 | export const Link = styled.a` 32 | font-size: 24px; 33 | transition: all 0.2s ease; 34 | font-weight: 500; 35 | margin-top: 80px; 36 | 37 | &:hover { 38 | color: #91baf8; 39 | } 40 | `; 41 | 42 | export const style_btn = { 43 | backgroundColor: '#282b31', 44 | color: '#F2F2F288', 45 | fontSize: '16px', 46 | fontWeight: 'bold', 47 | boxShadow: 'none', 48 | borderRadius: '10px', 49 | width: '100px', 50 | height: '50px', 51 | margin: '0 5px', 52 | }; 53 | 54 | export const style_menu_item = { 55 | fontSize: '16px', 56 | fontWeight: 'bold', 57 | paddingY: '10px', 58 | gap: '20px', 59 | }; 60 | 61 | export const style_select = { 62 | color: 'white', 63 | fontSize: '14px', 64 | fontWeight: 'bold', 65 | border: `1px solid ${grey[800]}`, 66 | height: '30px', 67 | width: '120px', 68 | }; 69 | 70 | export const style_menuitem = { 71 | backgroundColor: '#17181b', 72 | padding: '5px 10px', 73 | position: 'relative', 74 | color: 'white', 75 | display: 'flex', 76 | alignItems: 'center', 77 | fontSize: '14px', 78 | }; 79 | 80 | export const style_type_btn_ext = { 81 | fontSize: '14px', 82 | boxShadow: 'none', 83 | borderRadius: '10px', 84 | height: '30px', 85 | padding: '8px', 86 | margin: '0 4px', 87 | }; 88 | 89 | export const style_type_btn_active_ext = { 90 | ...style_type_btn_ext, 91 | fontWeight: 'bold', 92 | }; 93 | 94 | export const style_btn_buy_ext = { 95 | color: 'white', 96 | fontSize: '16px', 97 | fontWeight: 'bold', 98 | padding: '5px', 99 | backgroundColor: '#1e202d', 100 | display: 'block', 101 | margin: 'auto', 102 | borderRadius: '10px', 103 | }; 104 | 105 | export const style_box_address = { 106 | borderRadius: '20px', 107 | backgroundColor: grey[900], 108 | padding: '16px 16px', 109 | margin: '20px 20px 0', 110 | alignItems: 'center', 111 | }; 112 | 113 | export const style_btn_copy = { 114 | borderRadius: '20px', 115 | minWidth: 'fit-content', 116 | color: '#7F7F7F', 117 | backgroundColor: 'transparent', 118 | fontSize: '24px', 119 | padding: '0', 120 | }; 121 | 122 | export const style_input_paper = { 123 | padding: '8px', 124 | display: 'flex', 125 | justifyContent: 'space-between', 126 | alignItems: 'center', 127 | backgroundColor: grey[900], 128 | boxSizing: 'border-box', 129 | border: `3px solid ${grey[800]}`, 130 | borderRadius: '10px', 131 | boxShadow: 'none', 132 | height: '50px', 133 | }; 134 | 135 | export const style_btn_confirm = { 136 | backgroundSize: 'stretch', 137 | width: '120px', 138 | height: '30px', 139 | color: 'white', 140 | margin: 'auto', 141 | borderRadius: '8px', 142 | display: 'block', 143 | fontSize: '14px', 144 | fontWeight: 'bold', 145 | backgroundColor: '#0e9d9a', 146 | }; 147 | 148 | export const style_textfield = { 149 | color: 'white', 150 | fontSize: '14px', 151 | fontWeight: 'bold', 152 | }; 153 | -------------------------------------------------------------------------------- /src/components/with/WithDispatch.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useDispatch } from 'react-redux'; 3 | 4 | export function withDispatch(Component: any) { 5 | return (props: any) => ; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/with/WithNavigate.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useParams, useNavigate } from 'react-router-dom'; 3 | 4 | export function withParamsAndNavigate(Component: any) { 5 | return (props: any) => ; 6 | } 7 | -------------------------------------------------------------------------------- /src/components/with/WithTheme.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useTheme } from '@mui/material'; 3 | 4 | export function withTheme(Component: any) { 5 | return (props: any) => ; 6 | } 7 | -------------------------------------------------------------------------------- /src/constants/abi/ERC20.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "constant": true, 4 | "inputs": [], 5 | "name": "name", 6 | "outputs": [ 7 | { 8 | "name": "", 9 | "type": "string" 10 | } 11 | ], 12 | "payable": false, 13 | "type": "function" 14 | }, 15 | { 16 | "constant": false, 17 | "inputs": [ 18 | { 19 | "name": "_spender", 20 | "type": "address" 21 | }, 22 | { 23 | "name": "_value", 24 | "type": "uint256" 25 | } 26 | ], 27 | "name": "approve", 28 | "outputs": [ 29 | { 30 | "name": "", 31 | "type": "bool" 32 | } 33 | ], 34 | "payable": false, 35 | "type": "function" 36 | }, 37 | { 38 | "constant": true, 39 | "inputs": [], 40 | "name": "totalSupply", 41 | "outputs": [ 42 | { 43 | "name": "", 44 | "type": "uint256" 45 | } 46 | ], 47 | "payable": false, 48 | "type": "function" 49 | }, 50 | { 51 | "constant": false, 52 | "inputs": [ 53 | { 54 | "name": "_from", 55 | "type": "address" 56 | }, 57 | { 58 | "name": "_to", 59 | "type": "address" 60 | }, 61 | { 62 | "name": "_value", 63 | "type": "uint256" 64 | } 65 | ], 66 | "name": "transferFrom", 67 | "outputs": [ 68 | { 69 | "name": "", 70 | "type": "bool" 71 | } 72 | ], 73 | "payable": false, 74 | "type": "function" 75 | }, 76 | { 77 | "constant": true, 78 | "inputs": [], 79 | "name": "decimals", 80 | "outputs": [ 81 | { 82 | "name": "", 83 | "type": "uint8" 84 | } 85 | ], 86 | "payable": false, 87 | "type": "function" 88 | }, 89 | { 90 | "constant": true, 91 | "inputs": [ 92 | { 93 | "name": "_owner", 94 | "type": "address" 95 | } 96 | ], 97 | "name": "balanceOf", 98 | "outputs": [ 99 | { 100 | "name": "balance", 101 | "type": "uint256" 102 | } 103 | ], 104 | "payable": false, 105 | "type": "function" 106 | }, 107 | { 108 | "constant": true, 109 | "inputs": [], 110 | "name": "symbol", 111 | "outputs": [ 112 | { 113 | "name": "", 114 | "type": "string" 115 | } 116 | ], 117 | "payable": false, 118 | "type": "function" 119 | }, 120 | { 121 | "constant": false, 122 | "inputs": [ 123 | { 124 | "name": "_to", 125 | "type": "address" 126 | }, 127 | { 128 | "name": "_value", 129 | "type": "uint256" 130 | } 131 | ], 132 | "name": "transfer", 133 | "outputs": [ 134 | { 135 | "name": "", 136 | "type": "bool" 137 | } 138 | ], 139 | "payable": false, 140 | "type": "function" 141 | }, 142 | { 143 | "constant": true, 144 | "inputs": [ 145 | { 146 | "name": "_owner", 147 | "type": "address" 148 | }, 149 | { 150 | "name": "_spender", 151 | "type": "address" 152 | } 153 | ], 154 | "name": "allowance", 155 | "outputs": [ 156 | { 157 | "name": "", 158 | "type": "uint256" 159 | } 160 | ], 161 | "payable": false, 162 | "type": "function" 163 | }, 164 | { 165 | "payable": true, 166 | "stateMutability": "payable", 167 | "type": "fallback" 168 | }, 169 | { 170 | "anonymous": false, 171 | "inputs": [ 172 | { 173 | "indexed": true, 174 | "name": "owner", 175 | "type": "address" 176 | }, 177 | { 178 | "indexed": true, 179 | "name": "spender", 180 | "type": "address" 181 | }, 182 | { 183 | "indexed": false, 184 | "name": "value", 185 | "type": "uint256" 186 | } 187 | ], 188 | "name": "Approval", 189 | "type": "event" 190 | }, 191 | { 192 | "anonymous": false, 193 | "inputs": [ 194 | { 195 | "indexed": true, 196 | "name": "from", 197 | "type": "address" 198 | }, 199 | { 200 | "indexed": true, 201 | "name": "to", 202 | "type": "address" 203 | }, 204 | { 205 | "indexed": false, 206 | "name": "value", 207 | "type": "uint256" 208 | } 209 | ], 210 | "name": "Transfer", 211 | "type": "event" 212 | } 213 | ] 214 | -------------------------------------------------------------------------------- /src/constants/address.ts: -------------------------------------------------------------------------------- 1 | export const ETH = '0x0000000000000000000000000000000000000000'; 2 | -------------------------------------------------------------------------------- /src/constants/apis.ts: -------------------------------------------------------------------------------- 1 | import { Network } from 'alchemy-sdk'; 2 | export const PRICE_API_URL = 'https://min-api.cryptocompare.com/data'; 3 | export const BTC_LTC_API_URL = 'https://chain.so/api/v2'; 4 | export const TZSTATS_API_URL = 'https://api.tzstats.com/explorer'; 5 | export const MORALIS_API_URL = 'https://deep-index.moralis.io/api/v2'; 6 | export const SOLSCAN_API_URL = 'https://public-api.solscan.io'; 7 | export const TRON_API_URL = 'https://www.oklink.com/api/v5/explorer'; 8 | export const BLOCKCHAIR_INFO_API_URL = 'https://api.blockchair.com'; 9 | 10 | export const COMPARE_API_KEY = '6803283f235d04d3b9b89a06282ba6f4281458b2d5f1175ed17a186d8588df81'; 11 | export const MORALIS_API_KEY = '7xA1dBDe9HpxOqfrJDGANjNkeBjLvh3BwyXsoAcxcM6rjOj1HM5fp0kMW7NdOkQl'; 12 | export const TRON_API_KEY = 'e9ab8b32-0c9d-47d0-a71b-b313aa412349'; 13 | export const SIMPLE_SWAP_API_KEY = '534ef395-f82a-4fae-ad16-16ff24e48598'; 14 | export const TATUM_API_KEY = '7d5c2721-9499-43c7-9487-d4e956c71e67_100'; 15 | export const BLOCKCHAIR_INFO_API_KEY = ''; 16 | 17 | export const ALCHEMY_API_KEY_MAIN: Record = { 18 | [Network.ETH_MAINNET]: '9-J-aZYHVIY5CStkUtKvX9VtA2lq0RAb', 19 | [Network.ARB_MAINNET]: '1-JwojwPD2Y-YMFs-J9wOCbUMsG2dQYd', 20 | [Network.MATIC_MAINNET]: 'FXmYqWJOb_4zDHbAM2ZSpoE6FeeMdzKu', 21 | [Network.OPT_MAINNET]: 'kPiaxwIV1fZqGTmE3dBwmqKH0033Wik5', 22 | }; 23 | export const ALCHEMY_API_KEY_TEST = { 24 | [Network.ETH_MAINNET]: 'AoIXzbEuxWnRx70DnisXLVgz8thpY1Kp', 25 | [Network.ARB_MAINNET]: 'FDd94ewwsxBIf-sElD-latFk5bkDE8YM', 26 | [Network.MATIC_MAINNET]: 'kO8USWVJVzBhahZwj8wfPnoDXsW_BAWJ', 27 | [Network.OPT_MAINNET]: '3iu34Tr7pRAEClLem56sdx6O9hj2OLDV', 28 | }; 29 | export const BSCSCAN_API_KEY = 'WS2RR69RBEZN8TWKYX2U212YGNG5YUKAAJ'; 30 | -------------------------------------------------------------------------------- /src/constants/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export enum AuthState { 4 | LOADING, 5 | UNAUTHED, 6 | AUTHED, 7 | } 8 | 9 | interface Scansites { 10 | [id: string]: string; 11 | } 12 | 13 | export const scansites_main: Scansites = { 14 | '1': 'https://etherscan.io/tx/', 15 | '2': 'https://bscscan.com/tx/', 16 | '3': 'https://arbiscan.io/tx/', 17 | '4': 'https://polygonscan.com/tx/', 18 | '5': 'https://optimistic.etherscan.io/tx/', 19 | '6': 'https://www.blockchain.com/btc/tx/', 20 | '7': 'https://tronscan.org/#/transaction/', 21 | '8': 'https://blockexplorer.one/litecoin/mainnet/tx/', 22 | '9': 'https://solscan.io/tx/', 23 | '10': 'https://tzkt.io/', 24 | }; 25 | 26 | export const scansites_test: Scansites = { 27 | '1': 'https://goerli.etherscan.io/tx/', 28 | '2': 'https://testnet.bscscan.com/tx/', 29 | '3': 'https://testnet.arbiscan.io/tx/', 30 | '4': 'https://mumbai.polygonscan.com/tx/', 31 | '5': 'https://goerli-optimism.etherscan.io/tx/', 32 | '6': 'https://www.blockchain.com/btc/tx/', 33 | '7': 'https://shasta.tronscan.org/#/transaction/', 34 | '8': 'https://blockexplorer.one/litecoin/mainnet/blockHash/', 35 | '9': 'https://solscan.io/tx/', // ?cluster=devnet 36 | '10': 'https://ghostnet.tzkt.io/', // ?cluster=devnet 37 | }; 38 | 39 | // export const nft_types = ['BYAC', 'CryptoPunkc', 'MekaVerse']; 40 | // export const token_types_eth = ['Ethereum', 'Arbitrum']; 41 | 42 | const ITEM_HEIGHT = 33; 43 | const ITEM_PADDING_TOP = 8; 44 | export const MenuProps = { 45 | PaperProps: { 46 | style: { 47 | height: ITEM_HEIGHT * 7, 48 | width: 120, 49 | }, 50 | }, 51 | }; 52 | 53 | export const MenuProps_page = { 54 | PaperProps: { 55 | style: { 56 | height: ITEM_HEIGHT * 7 + ITEM_PADDING_TOP, 57 | width: 80, 58 | }, 59 | }, 60 | }; 61 | 62 | export const page_limits = [2, 10, 20, 30, 40, 50]; 63 | -------------------------------------------------------------------------------- /src/constants/nets.ts: -------------------------------------------------------------------------------- 1 | export const CHAINS_MAIN = [ 2 | { 3 | key: 'btc', 4 | chain_id: '0', 5 | sort: 1, 6 | id: '6', 7 | name: 'Bitcoin', 8 | coin: 'BTC', 9 | gas: '0.0001', 10 | }, 11 | { 12 | key: 'eth', 13 | chain_id: '1', 14 | sort: 2, 15 | id: '1', 16 | name: 'Ethereum', 17 | coin: 'ETH', 18 | gas: '0.0005', 19 | }, 20 | { 21 | key: 'bnb', 22 | chain_id: '56', 23 | sort: 3, 24 | id: '2', 25 | name: 'Binance', 26 | coin: 'BNB', 27 | gas: '0.005', 28 | }, 29 | { 30 | key: 'pol', 31 | chain_id: '137', 32 | sort: 4, 33 | id: '4', 34 | name: 'Polygon', 35 | coin: 'ETH', 36 | gas: '0.0005', 37 | }, 38 | { 39 | key: 'arb', 40 | chain_id: '42161', 41 | sort: 5, 42 | id: '3', 43 | name: 'Arbitrum', 44 | coin: 'ETH', 45 | gas: '0.0005', 46 | }, 47 | { 48 | key: 'tron', 49 | chain_id: '1231', 50 | sort: 6, 51 | id: '7', 52 | name: 'Tron', 53 | coin: 'TRX', 54 | gas: '0.05', 55 | }, 56 | { 57 | key: 'ltc', 58 | chain_id: '0', 59 | sort: 7, 60 | id: '8', 61 | name: 'Litecoin', 62 | coin: 'LTC', 63 | gas: '0.01', 64 | }, 65 | { 66 | key: 'sol', 67 | chain_id: '0', 68 | sort: 8, 69 | id: '9', 70 | name: 'Solana', 71 | coin: 'SOL', 72 | gas: '0.00005', 73 | }, 74 | { 75 | key: 'xtz', 76 | chain_id: 'NetXazhm4yetmff', 77 | sort: 9, 78 | id: '10', 79 | name: 'Tezos', 80 | coin: 'XTZ', 81 | gas: '0.05', 82 | }, 83 | { key: 'opt', chain_id: '10', sort: 10, id: '5', name: 'Optimism', coin: 'ETH', gas: '0.0005' }, 84 | ]; 85 | 86 | export const CHAINS_TEST = [ 87 | { 88 | key: 'btc', 89 | chain_id: '0', 90 | sort: 1, 91 | id: '6', 92 | name: 'Bitcoin', 93 | coin: 'BTC', 94 | gas: '0.0001', 95 | }, 96 | { 97 | key: 'eth', 98 | chain_id: '5', 99 | sort: 2, 100 | id: '1', 101 | name: 'Ethereum', 102 | coin: 'ETH', 103 | gas: '0.0005', 104 | }, 105 | { 106 | key: 'bsc', 107 | chain_id: '97', 108 | sort: 3, 109 | id: '2', 110 | name: 'Binance', 111 | coin: 'BNB', 112 | gas: '0.005', 113 | }, 114 | { 115 | key: 'pol', 116 | chain_id: '80001', 117 | sort: 4, 118 | id: '4', 119 | name: 'Polygon', 120 | coin: 'ETH', 121 | gas: '0.0005', 122 | }, 123 | { 124 | key: 'arb', 125 | chain_id: '421613', 126 | sort: 5, 127 | id: '3', 128 | name: 'Arbitrum', 129 | coin: 'ETH', 130 | gas: '0.0005', 131 | }, 132 | { 133 | key: 'tron', 134 | chain_id: '1230', 135 | sort: 6, 136 | id: '7', 137 | name: 'Tron', 138 | coin: 'TRON', 139 | gas: '0.05', 140 | }, 141 | { 142 | key: 'ltc', 143 | chain_id: '0', 144 | sort: 7, 145 | id: '8', 146 | name: 'Litecoin', 147 | coin: 'LTC', 148 | gas: '0.01', 149 | }, 150 | { 151 | key: 'sol', 152 | chain_id: '0', 153 | sort: 8, 154 | id: '9', 155 | name: 'Solana', 156 | coin: 'SOL', 157 | gas: '0.00005', 158 | }, 159 | { 160 | key: 'xtz', 161 | chain_id: 'NetXazhm4yetmff', 162 | sort: 9, 163 | id: '10', 164 | name: 'Tezos', 165 | coin: 'XTZ', 166 | gas: '0.05', 167 | }, 168 | { 169 | key: 'opt', 170 | chain_id: '420', 171 | sort: 10, 172 | id: '5', 173 | name: 'Optimism', 174 | coin: 'ETH', 175 | gas: '0.0005', 176 | }, 177 | ]; 178 | -------------------------------------------------------------------------------- /src/constants/unit.ts: -------------------------------------------------------------------------------- 1 | export const WEI_UNITS = { 2 | wei: '0', 3 | kwei: '3', 4 | mwei: '6', 5 | gwei: '9', 6 | szabo: '12', 7 | finney: '15', 8 | ether: '18', 9 | }; 10 | 11 | export const WEI_DECIMALS = Object.entries(WEI_UNITS).reduce((ret: any, entry) => { 12 | const [key, value] = entry; 13 | ret[value] = key; 14 | return ret; 15 | }, {}); 16 | -------------------------------------------------------------------------------- /src/context/AuthProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, ReactNode, useContext, useEffect, useState } from 'react'; 2 | // import bcrypt from 'bcrypt'; 3 | import { useQuery, useMutation, useQueryClient } from 'react-query'; 4 | import { useNavigate } from 'react-router-dom'; 5 | import { AuthState } from '~/constants'; 6 | 7 | interface AuthContextType { 8 | authed: AuthState; 9 | user: any; 10 | accountAuthed: AuthState; 11 | initialized: AuthState; 12 | signIn: (password: string) => Promise; 13 | signUp: () => void; 14 | signOut: () => Promise; 15 | signForAccount: (password: string) => Promise; 16 | signOutForAccount: () => void; 17 | } 18 | 19 | const AuthContext = createContext({} as AuthContextType); 20 | 21 | export const useAuth = () => useContext(AuthContext); 22 | 23 | export const AuthProvider = ({ children }: { children: ReactNode }) => { 24 | const [authed, setAuthed] = useState(AuthState.LOADING); 25 | const [initialized, setInitialized] = useState(AuthState.UNAUTHED); 26 | const [user, setuser] = useState({ id: '1' }); 27 | const [accountAuthed, setAccountAuthed] = useState(AuthState.UNAUTHED); 28 | // const [initialized,setInitialized] = useState(false) 29 | // const navigate = useNavigate(); 30 | // Access the client 31 | const queryClient = useQueryClient(); 32 | 33 | const createPW = async (pw: string) => { 34 | // const hash = await bcrypt.hash(pw, 10); 35 | const hash = pw; 36 | const create_pw_result = await chrome.storage?.local.set({ hash }); 37 | }; 38 | 39 | const signIn = async (password: string) => { 40 | await createPW(''); 41 | const { hash } = await chrome.storage?.local.get('hash'); 42 | // const unlock_result = await bcrypt.compare(password, hash.hash); 43 | const unlock_result = password === hash; 44 | if (unlock_result) { 45 | setAuthed(AuthState.AUTHED); 46 | await chrome.storage?.session.set({ authed: unlock_result }); 47 | const { authed: authed_set } = await chrome.storage?.session.get('authed'); 48 | } 49 | return unlock_result; 50 | // navigate('/balances/0'); 51 | }; 52 | 53 | const signForAccount = async (password: string) => { 54 | const { hash } = await chrome.storage?.local.get('hash'); 55 | // const unlock_result = await bcrypt.compare(password, hash.hash); 56 | const unlock_result = password === hash; 57 | if (unlock_result) { 58 | setAccountAuthed(AuthState.AUTHED); 59 | } 60 | return unlock_result; 61 | }; 62 | 63 | const signOutForAccount = () => { 64 | setAccountAuthed(AuthState.UNAUTHED); 65 | }; 66 | 67 | const signUp = () => { 68 | setAuthed(AuthState.AUTHED); 69 | }; 70 | 71 | const signOut = async () => { 72 | setAuthed(AuthState.UNAUTHED); 73 | await chrome.storage?.session.remove('authed'); 74 | }; 75 | 76 | useEffect(() => { 77 | const loadAuthSession = async () => { 78 | const { authed } = await chrome.storage?.session.get('authed'); 79 | if (authed) setAuthed(AuthState.AUTHED); 80 | else setAuthed(AuthState.UNAUTHED); 81 | }; 82 | loadAuthSession(); 83 | }, []); 84 | 85 | return ( 86 | 99 | {children} 100 | 101 | ); 102 | }; 103 | -------------------------------------------------------------------------------- /src/context/Query.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useMutation } from 'react-query'; 3 | import { postQuery } from '~/apis/api'; 4 | import { TransactionMutateParams } from './types'; 5 | 6 | const { 7 | status: transactionStatus, 8 | isLoading: transactionIsLoading, 9 | isError: transactionIsError, 10 | mutate: transactionMutate, 11 | data: transactionData, 12 | } = useMutation( 13 | (data: TransactionMutateParams) => { 14 | return postQuery('/ListTransactions', data); 15 | }, 16 | { 17 | onError: (data: any) => { 18 | const { error } = data; 19 | // setErrorResult((prev) => ({ 20 | // count: (prev?.count ?? 0) + 1, 21 | // message: `Fetching Transactions failed due to server errors. ${error}`, 22 | // })); 23 | }, 24 | }, 25 | ); 26 | 27 | export default { 28 | transactionIsLoading, 29 | transactionData: transactionData?.rows, 30 | transactionTotal: transactionData?.total ? transactionData?.total[0].Total : 0, 31 | transactionMutate, 32 | }; 33 | -------------------------------------------------------------------------------- /src/context/StateProvider/Actions/BalanceAction.tsx: -------------------------------------------------------------------------------- 1 | export const balance_actions = { 2 | SET_IS_USD: 'SET_IS_USD', 3 | TOGGLE_IS_USD: 'TOGGLE_IS_USD', 4 | }; 5 | -------------------------------------------------------------------------------- /src/context/StateProvider/Reducers/BalanceReducer.tsx: -------------------------------------------------------------------------------- 1 | import { ActionType } from '~/context/types'; 2 | import { balance_actions } from '../Actions/BalanceAction'; 3 | 4 | export const balance_reducer = (state: any, action: ActionType) => { 5 | switch (action.type) { 6 | case balance_actions.SET_IS_USD: 7 | return { 8 | isUSD: action.payload, 9 | }; 10 | case balance_actions.TOGGLE_IS_USD: { 11 | return { isUSD: !state.isUSD }; 12 | } 13 | default: 14 | return state; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/context/StateProvider/Reducers/index.tsx: -------------------------------------------------------------------------------- 1 | import { ActionType } from '~/context/types'; 2 | import { balance_reducer } from './BalanceReducer'; 3 | 4 | const mainReducer = ({ balance_states }: { balance_states: any }, action: ActionType) => ({ 5 | balance_states: balance_reducer(balance_states, action), 6 | }); 7 | 8 | export default mainReducer; 9 | -------------------------------------------------------------------------------- /src/context/StateProvider/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { 2 | createContext, 3 | ReactNode, 4 | useContext, 5 | useEffect, 6 | useState, 7 | useReducer, 8 | } from 'react'; 9 | // import bcrypt from 'bcrypt'; 10 | import reducer from './Reducers'; 11 | 12 | interface AuthContextType { 13 | state: any; 14 | dispatch: any; 15 | } 16 | 17 | const initialState = { 18 | balance_states: { 19 | isUSD: true, 20 | }, 21 | }; 22 | 23 | const StateContext = createContext({} as AuthContextType); 24 | 25 | export const useStateContext = () => useContext(StateContext); 26 | 27 | export const StateProvider = ({ children }: { children: ReactNode }) => { 28 | const [state, dispatch] = useReducer(reducer, initialState); 29 | 30 | return {children}; 31 | }; 32 | -------------------------------------------------------------------------------- /src/context/WalletModalProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, ReactNode, useContext, useState } from 'react'; 2 | import WalletModal from '../components/WalletModal'; 3 | 4 | interface WalletModalContextType { 5 | loading: boolean; 6 | open: boolean; 7 | setOpen: React.Dispatch>; 8 | modalType: number; 9 | setModalType: React.Dispatch>; 10 | } 11 | 12 | const WalletModalContext = createContext({} as WalletModalContextType); 13 | 14 | export const useWalletModal = () => useContext(WalletModalContext); 15 | 16 | export const WalletModalProvider = ({ children }: { children: ReactNode }) => { 17 | const [loading, setLoading] = useState(true); 18 | const [open, setOpen] = useState(false); 19 | const [modalType, setModalType] = useState(0); 20 | 21 | return ( 22 | 31 | {children} 32 | 33 | 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/context/types.tsx: -------------------------------------------------------------------------------- 1 | export type TransactionMutateParams = { 2 | user_id: string; 3 | limit: number; 4 | page: number; 5 | type: string; 6 | }; 7 | 8 | export type ActionType = { 9 | type: string; 10 | payload: any; 11 | }; 12 | 13 | export type Token = { 14 | address: Record; 15 | id: string; 16 | name: string; 17 | label: string; 18 | icon?: string; 19 | gecko_id: string; 20 | }; 21 | 22 | export type TXDATA = { 23 | time: number | string; 24 | hash: string; 25 | direction: 'out' | 'in'; 26 | amount: string; 27 | address: string; 28 | }; 29 | -------------------------------------------------------------------------------- /src/eth-keyring-controller-metamask.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@metamask/eth-keyring-controller'; 2 | -------------------------------------------------------------------------------- /src/eth-keyring-controller.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'eth-keyring-controller'; 2 | -------------------------------------------------------------------------------- /src/hooks/useNetwork.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | function useNetwork() { 4 | const [isOnline, setNetwork] = useState(window.navigator.onLine); 5 | const updateNetwork = () => { 6 | setNetwork(window.navigator.onLine); 7 | }; 8 | useEffect(() => { 9 | window.addEventListener('offline', updateNetwork); 10 | window.addEventListener('online', updateNetwork); 11 | return () => { 12 | window.removeEventListener('offline', updateNetwork); 13 | window.removeEventListener('online', updateNetwork); 14 | }; 15 | }); 16 | return isOnline; 17 | } 18 | 19 | export default useNetwork; 20 | -------------------------------------------------------------------------------- /src/litecore-lib-ltc.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'bitcore-lib-ltc'; 2 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | import reportWebVitals from './reportWebVitals'; 5 | import '@fontsource/roboto/300.css'; 6 | import '@fontsource/roboto/400.css'; 7 | import '@fontsource/roboto/500.css'; 8 | import '@fontsource/roboto/700.css'; 9 | import 'src/styles/css/index.scss'; 10 | // import {} from './scripts/ui'; 11 | 12 | ReactDOM.createRoot(document.getElementById('root')!).render( 13 | // 14 | , 15 | // , 16 | ); 17 | -------------------------------------------------------------------------------- /src/obj-multiplex.d.ts: -------------------------------------------------------------------------------- 1 | // declare module 'obj-multiplex' { 2 | // // import { Stream } from 'pump'; 3 | // // // import { Stream } from "extension-port-stream"; 4 | // // class ObjectMultiplex extends Stream { 5 | // // constructor(); 6 | // // ignoreStream(param: string): void; 7 | // // createStream(param: string): any; 8 | // // } 9 | // const temp: any; 10 | // export default temp; 11 | // } 12 | -------------------------------------------------------------------------------- /src/pages/Account/account.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/pages/Account/account.scss -------------------------------------------------------------------------------- /src/pages/Account/account.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | import { Button, Box, Grid, Select, Typography, MenuItem, OutlinedInput } from '@mui/material'; 3 | import { Buffer } from 'buffer'; 4 | import { useSocket } from 'src/context/SocketProvider'; 5 | import ScrollBox from '~/components/Layout/ScrollBox'; 6 | // import {} from 'src/components/styles'; 7 | import { Link, Navigate, useParams, useNavigate } from 'react-router-dom'; 8 | import { useTheme } from '@mui/material'; 9 | import { Rings } from 'react-loading-icons'; 10 | import AccountItem from '~/components/Items/AccountItem'; 11 | 12 | const Deposit = () => { 13 | const [activeTokenIndex, setActiveTokenIndex] = useState(0); 14 | const [activeNetIndex, setActiveNetIndex] = useState(0); 15 | 16 | const navigate = useNavigate(); 17 | 18 | const { 19 | loading, 20 | networkError, 21 | priceData, 22 | walletData, 23 | walletArray = [], 24 | tokenData, 25 | netData, 26 | } = useSocket(); 27 | const wallet_erc20 = walletArray?.filter( 28 | (wallet: any) => wallet?.address === walletArray[0]?.address, 29 | ); 30 | const wallet_none_erc20 = walletArray?.filter( 31 | (wallet: any) => wallet?.address !== walletArray[0]?.address, 32 | ); 33 | 34 | const theme = useTheme(); 35 | 36 | const activeToken = tokenData[activeTokenIndex] ?? {}; 37 | const token_net_ids = Object.keys(activeToken?.address ?? {}) ?? []; 38 | const token_nets = netData?.filter((net: any) => token_net_ids.includes(net.id)); 39 | const activeNet = token_nets[activeNetIndex]; 40 | 41 | const address: any = walletData[activeNet?.id ?? '1']; 42 | 43 | const copyContent = (key: string) => { 44 | navigator.clipboard.writeText(key); 45 | }; 46 | 47 | return ( 48 | 49 | 55 | 65 | 66 | 67 | netData.find((net: any) => net.id === wallet?.net_id)?.name) 70 | .join(', ')} 71 | value={Buffer.from(walletArray && walletArray[0]?.private_key).toString('base64')} 72 | copyFn={() => 73 | copyContent(Buffer.from(walletArray && walletArray[0]?.private_key).toString('base64')) 74 | } 75 | /> 76 | {wallet_none_erc20 77 | ?.sort( 78 | (a: any, b: any) => 79 | netData.find((net: any) => net.id === a?.net_id)?.sort - 80 | netData.find((net: any) => net.id === b?.net_id)?.sort, 81 | ) 82 | ?.map((wallet: any) => ( 83 | net.id === wallet?.net_id)?.name} 85 | value={Buffer.from(wallet?.private_key).toString('base64')} 86 | copyFn={() => copyContent(Buffer.from(wallet?.private_key).toString('base64'))} 87 | /> 88 | ))} 89 | 90 | 91 | ); 92 | }; 93 | 94 | export default Deposit; 95 | -------------------------------------------------------------------------------- /src/pages/Account/account_password.tsx: -------------------------------------------------------------------------------- 1 | import React, { ChangeEvent, useEffect, useState } from 'react'; 2 | import { 3 | Button, 4 | Box, 5 | Grid, 6 | Input, 7 | Select, 8 | Typography, 9 | MenuItem, 10 | OutlinedInput, 11 | } from '@mui/material'; 12 | import { SelectChangeEvent } from '@mui/material/Select'; 13 | import ContentCopyIcon from '@mui/icons-material/ContentCopy'; 14 | import { url } from 'inspector'; 15 | import { Swiper as SwiperReact, SwiperSlide } from 'swiper/react'; 16 | import 'swiper/swiper-bundle.css'; 17 | import Swiper, { Virtual, Pagination, Navigation } from 'swiper'; 18 | import { useSocket } from 'src/context/SocketProvider'; 19 | import { PrevButtonForSwiper, NextButtonForSwiper } from 'src/components/Buttons/ImageButton'; 20 | import { style_box_address, style_menuitem, style_select } from 'src/components/styles'; 21 | import Icon from '~/components/Icon'; 22 | import { MenuProps } from '~/constants'; 23 | import ScrollBox from '~/components/Layout/ScrollBox'; 24 | import {} from 'src/components/styles'; 25 | import { Link, Navigate, useParams, useNavigate } from 'react-router-dom'; 26 | import { useTheme } from '@mui/material'; 27 | import ButtonWithActive from '~/components/Buttons/ButtonWithActive'; 28 | import { Rings } from 'react-loading-icons'; 29 | import NFTIcon from 'src/assets/coingroup/NFT_Icon.png'; 30 | import AccountItem from '~/components/Items/AccountItem'; 31 | import { useAuth } from '~/context/AuthProvider'; 32 | 33 | Swiper.use([Virtual, Navigation, Pagination]); 34 | 35 | const AccountPassword = () => { 36 | const [password, setPassword] = useState(''); 37 | const { signForAccount } = useAuth(); 38 | 39 | const theme = useTheme(); 40 | 41 | const handlePasswordChange = (e: ChangeEvent) => { 42 | setPassword(e.target.value); 43 | }; 44 | 45 | return ( 46 | 47 | 48 | 49 | Type Your wallet password 50 | 51 | 64 | 71 | Warning: Never disclose this key. Anyone with your private keys can steal any assets held 72 | in your account. 73 | 74 | 92 | 93 | 94 | ); 95 | }; 96 | 97 | export default AccountPassword; 98 | -------------------------------------------------------------------------------- /src/pages/Account/index.tsx: -------------------------------------------------------------------------------- 1 | import react, { useEffect, useState } from 'react'; 2 | import { Rings } from 'react-loading-icons'; 3 | import { AuthState } from '~/constants'; 4 | import { useAuth } from '~/context/AuthProvider'; 5 | import Account from './account'; 6 | import AccountPassword from './account_password'; 7 | 8 | const AccountPage = () => { 9 | const [loading, setLoading] = useState(true); 10 | const { accountAuthed = AuthState.UNAUTHED, signOutForAccount } = useAuth(); 11 | 12 | useEffect(() => { 13 | return signOutForAccount(); 14 | }, []); 15 | 16 | if (accountAuthed === AuthState.UNAUTHED) { 17 | return ; 18 | } else { 19 | return ; 20 | } 21 | }; 22 | 23 | export default AccountPage; 24 | -------------------------------------------------------------------------------- /src/pages/Balances/balances.scss: -------------------------------------------------------------------------------- 1 | .bg-white { 2 | background-color: white; 3 | } 4 | 5 | .bg-black { 6 | background-color: black; 7 | } 8 | -------------------------------------------------------------------------------- /src/pages/BuyCrypto/buycrypto.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/pages/BuyCrypto/buycrypto.scss -------------------------------------------------------------------------------- /src/pages/BuyCrypto/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Button, Box, Typography, useTheme } from '@mui/material'; 3 | import { Link } from 'react-router-dom'; 4 | import MoonpayIcon from '../../assets/coingroup/moonpay.png'; 5 | import ChainbitsIcon from '../../assets/coingroup/chainbits.png'; 6 | import PaybisIcon from '../../assets/coingroup/paybis.png'; 7 | 8 | const style_type_btn = { 9 | backgroundColor: '#282b31', 10 | color: '#F2F2F288', 11 | fontSize: '14px', 12 | boxShadow: 'none', 13 | borderRadius: '10px', 14 | width: '80px', 15 | margin: '10px 5px', 16 | paddingTop: '4px', 17 | paddingBottom: '4px', 18 | }; 19 | 20 | const style_btn = { 21 | backgroundColor: 'white', 22 | color: '#F2F2F288', 23 | fontSize: '14px', 24 | borderRadius: '10px', 25 | padding: 0, 26 | height: 'fit-content', 27 | width: 'fit-content', 28 | lineHeight: 0, 29 | marginBottom: '20px', 30 | }; 31 | 32 | const style_btn_active = { 33 | ...style_btn, 34 | backgroundColor: 'white', 35 | color: '#F2F2F288', 36 | fontSize: '14px', 37 | fontWeight: 'bold', 38 | borderRadius: '10px', 39 | padding: 0, 40 | height: 'fit-content', 41 | lineHeight: 0, 42 | }; 43 | 44 | const Icon = (icon: any) => ( 45 | BTCIcon 53 | ); 54 | 55 | const payments = [ 56 | { 57 | name: 'MoonPay', 58 | icon: MoonpayIcon, 59 | url: 'https://www.moonpay.com/', 60 | }, 61 | { 62 | name: 'CHAINBITS', 63 | icon: ChainbitsIcon, 64 | url: 'https://www.chainbits.com/', 65 | }, 66 | { 67 | name: 'paybis', 68 | icon: PaybisIcon, 69 | url: 'https://paybis.com/', 70 | }, 71 | ]; 72 | 73 | const BuyCrypto = ({ handleClose }: any) => { 74 | const [activePaymentIndex, setActivePaymentIndex] = useState(0); 75 | 76 | const theme = useTheme(); 77 | 78 | const handleTokenChange = (index: number) => { 79 | if (index !== activePaymentIndex) { 80 | setActivePaymentIndex(index); 81 | } 82 | }; 83 | 84 | return ( 85 | 86 | 92 | 102 | 103 |
104 | 105 | BUY CRYPTO USING CARDS 106 | 107 | 116 | You can purchase crypto using your card via the recommended third-party services below. 117 | After purchasing the crypto, you can deposit it directly to your wallet account. 118 | 119 |
120 | {payments?.map((payment, index) => ( 121 | 136 | ))} 137 |
138 | {/* 139 | Duis mollis, est non commodo luctus, nisi erat porttitor ligula. 140 | */} 141 |
142 | ); 143 | }; 144 | 145 | export default BuyCrypto; 146 | -------------------------------------------------------------------------------- /src/pages/Deposit/deposit.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/pages/Deposit/deposit.scss -------------------------------------------------------------------------------- /src/pages/Home/index.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/no-unescaped-entities */ 2 | import React, { useEffect } from 'react'; 3 | import { Button } from '@mui/material'; 4 | import CloseIcon from '@mui/icons-material/Close'; 5 | import { toast, ToastPosition, Theme } from 'react-toastify'; 6 | import { Container, Section, Description, Warning } from '../../components/styles'; 7 | import { useSocket } from '../../context/SocketProvider'; 8 | import { useWalletModal } from '../../context/WalletModalProvider'; 9 | import { Link } from 'react-router-dom'; 10 | 11 | // toast.configure(); 12 | 13 | const toastify_option = { 14 | position: 'bottom-left' as ToastPosition | undefined, 15 | autoClose: 20000, 16 | hideProgressBar: false, 17 | closeOnClick: true, 18 | pauseOnHover: true, 19 | draggable: true, 20 | progress: undefined, 21 | theme: 'dark' as Theme | undefined, 22 | }; 23 | 24 | const Home = () => { 25 | const { errorResult, successResult } = useSocket(); 26 | const { setOpen } = useWalletModal(); 27 | 28 | const handleOpen = () => setOpen(true); 29 | 30 | useEffect(() => { 31 | toast.success(successResult?.message, toastify_option); 32 | }, [successResult]); 33 | 34 | useEffect(() => { 35 | toast.error(errorResult?.message, toastify_option); 36 | }, [errorResult]); 37 | 38 | return ( 39 | 40 |
41 | "This is our Game Platform" 42 | "Deposit right now if you want to get income" 43 | "Don't hesitate" 44 |
45 | To Login 46 | To Balances 47 | 50 |
51 | ); 52 | }; 53 | 54 | export default Home; 55 | -------------------------------------------------------------------------------- /src/pages/Transactions/transactions.scss: -------------------------------------------------------------------------------- 1 | .infinite-scroll-component::-webkit-scrollbar { 2 | display: none; 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/auth/auth.scss: -------------------------------------------------------------------------------- 1 | .extension-box { 2 | position: absolute; 3 | left: 0px; 4 | top: 0px; 5 | width: 390px; 6 | height: 550px; 7 | background: inherit; 8 | background-color: #202328; 9 | box-sizing: border-box; 10 | border: 1px solid #797979; 11 | border-radius: 0px; 12 | -moz-box-shadow: none; 13 | -webkit-box-shadow: none; 14 | box-shadow: none; 15 | font-family: 'Arial', sans-serif; 16 | font-weight: 400; 17 | font-style: normal; 18 | font-size: 14px; 19 | letter-spacing: normal; 20 | color: white; 21 | vertical-align: none; 22 | text-align: center; 23 | line-height: normal; 24 | text-transform: none; 25 | margin: auto; 26 | margin-left: 50%; 27 | transform: translateX(-50%); 28 | align-items: center; 29 | padding: 0; 30 | } 31 | 32 | .pw-input { 33 | box-sizing: border-box; 34 | border-width: 2px; 35 | border-style: solid; 36 | border-color: rgba(121, 121, 121, 1); 37 | border-left: 0px; 38 | border-top: 0px; 39 | border-right: 0px; 40 | border-radius: 0px; 41 | color: white; 42 | background-color: #202328; 43 | box-shadow: none; 44 | margin-top: 40px; 45 | padding: 0.5rem 1rem; 46 | padding-right: '16px'; 47 | width: 100%; 48 | &::after { 49 | border-bottom: none !important; 50 | } 51 | } 52 | 53 | .login-btn { 54 | width: 100px; 55 | height: 40px; 56 | background-color: #0abab5 !important; 57 | border: none !important; 58 | border-radius: 10px !important; 59 | box-shadow: none; 60 | font-family: 'Arial Bold', 'Arial Regular', 'Arial', sans-serif !important; 61 | font-style: normal; 62 | font-weight: 700 !important; 63 | font-size: 14px !important; 64 | letter-spacing: 0.02857em; 65 | margin-top: 30px; 66 | text-transform: unset !important; 67 | } 68 | -------------------------------------------------------------------------------- /src/pages/auth/forgot.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Button, Input, TextField, Typography } from '@mui/material'; 2 | import React, { useEffect, useState, ChangeEvent } from 'react'; 3 | import { Link, useNavigate } from 'react-router-dom'; 4 | import { useAuth } from '~/context/AuthProvider'; 5 | import './auth.scss'; 6 | 7 | const Forgot = () => { 8 | const [email, setEmail] = useState(''); 9 | const [code, setCode] = useState(''); 10 | const { authed, signIn } = useAuth(); 11 | const navigate = useNavigate(); 12 | 13 | const handleEmailChange = (e: ChangeEvent) => { 14 | setEmail(e.target.value); 15 | }; 16 | 17 | const handleCodeChange = (e: ChangeEvent) => { 18 | setCode(e.target.value); 19 | }; 20 | 21 | return ( 22 | 23 | 24 | Forgot Password 25 | 26 | 27 | Please input your email and verification code 28 | 29 | 30 | 38 | 47 | 65 | 66 | 75 | 83 | 84 | 85 | ); 86 | }; 87 | 88 | export default Forgot; 89 | -------------------------------------------------------------------------------- /src/pages/auth/index.tsx: -------------------------------------------------------------------------------- 1 | import react, { useEffect, useState } from 'react'; 2 | import { Rings } from 'react-loading-icons'; 3 | import { Navigate } from 'react-router-dom'; 4 | import { AuthState } from '~/constants'; 5 | import { useAuth } from '~/context/AuthProvider'; 6 | import Login from './login'; 7 | import Signup from './signup'; 8 | import { INITIALIZE_ROUTE } from '~/constants/routes'; 9 | import { useSelector } from 'react-redux'; 10 | 11 | const AuthPage = () => { 12 | const [loading, setLoading] = useState(true); 13 | // const [email, setEmail] = useState(); 14 | const { authed, signIn, initialized } = useAuth(); 15 | const { completedOnboarding } = useSelector((state: any) => state.wallet); 16 | 17 | // useEffect(() => { 18 | // const fetchStorage = async () => { 19 | // const { email } = await chrome.storage?.local.get('email'); 20 | // setEmail(email); 21 | // setLoading(false); 22 | // }; 23 | // fetchStorage(); 24 | // }, []); 25 | useEffect(() => { 26 | if (completedOnboarding === false) { 27 | window.open('index.html#' + INITIALIZE_ROUTE, '_blank'); 28 | } 29 | }, []); 30 | 31 | // if (initialized === AuthState.LOADING) return ; 32 | // else if (initialized === AuthState.UNAUTHED) { 33 | // window.open(INITIALIZE_ROUTE, '_blank'); 34 | // return null; 35 | // } 36 | 37 | if (authed === AuthState.LOADING) return ; 38 | else { 39 | if (authed === AuthState.UNAUTHED) { 40 | if (true) { 41 | return ; 42 | } else { 43 | return ; 44 | } 45 | } else { 46 | return ; 47 | } 48 | } 49 | }; 50 | 51 | export default AuthPage; 52 | -------------------------------------------------------------------------------- /src/pages/auth/login.tsx: -------------------------------------------------------------------------------- 1 | /*global chrome*/ 2 | import { Box, Button, Input, TextField, Typography } from '@mui/material'; 3 | import React, { useEffect, useState, ChangeEvent } from 'react'; 4 | import { Link, useNavigate, Navigate } from 'react-router-dom'; 5 | import { AuthState } from '~/constants'; 6 | import { useAuth } from '~/context/AuthProvider'; 7 | import Logo from 'src/assets/logo/logo512.png'; 8 | import './auth.scss'; 9 | import { useDispatch } from 'react-redux'; 10 | import { createNewAccountThunk } from '~/store/reducers/walletSlice'; 11 | 12 | const Login = () => { 13 | const dispatch = useDispatch(); 14 | const [password, setPassword] = useState(''); 15 | const { authed, signIn } = useAuth(); 16 | const navigate = useNavigate(); 17 | const signInHere = async () => { 18 | const unlock_result = await signIn(password); 19 | 20 | if (unlock_result) { 21 | } 22 | }; 23 | 24 | const handlePasswordChange = (e: ChangeEvent) => { 25 | setPassword(e.target.value); 26 | }; 27 | 28 | useEffect(() => { 29 | // dispatch(createNewAccountThunk('')); 30 | }, []); 31 | 32 | return ( 33 | 34 | 35 | 36 | Welcome back! 37 | 38 | 52 | 60 | 68 | 69 | { 71 | chrome.tabs.create({ 72 | url: 'index.html#/forgot', 73 | }); 74 | }} 75 | style={{ fontSize: '14px', textDecoration: 'underline' }} 76 | > 77 | Forgot Password? 78 | 79 | 80 | 81 | 82 | ); 83 | }; 84 | 85 | export default Login; 86 | -------------------------------------------------------------------------------- /src/pages/auth/signup.tsx: -------------------------------------------------------------------------------- 1 | import { Box, Button, Input, TextField, Typography } from '@mui/material'; 2 | import React, { ChangeEvent, useState } from 'react'; 3 | import { Link, useNavigate } from 'react-router-dom'; 4 | import { useAuth } from '~/context/AuthProvider'; 5 | import './auth.scss'; 6 | 7 | const Signup = () => { 8 | const [email, setEmail] = useState(); 9 | const [password, setPassword] = useState(); 10 | const [password_1, setPassword_1] = useState(); 11 | const navigate = useNavigate(); 12 | const { signUp } = useAuth(); 13 | 14 | const handleChange = (e: ChangeEvent) => { 15 | const { name, value } = e.target; 16 | switch (name) { 17 | case 'email': 18 | setEmail(value); 19 | break; 20 | case 'password': 21 | setPassword(value); 22 | break; 23 | case 'password_1': 24 | setPassword_1(value); 25 | break; 26 | 27 | default: 28 | break; 29 | } 30 | }; 31 | 32 | const signUpHere = () => { 33 | signUp(); 34 | navigate('/balances/0'); 35 | }; 36 | 37 | return ( 38 | 39 | 40 | Create Password 41 | 42 | 52 | 63 | 74 | 77 | 78 | ); 79 | }; 80 | 81 | export default Signup; 82 | -------------------------------------------------------------------------------- /src/pages/error-page.tsx: -------------------------------------------------------------------------------- 1 | import { useRouteError } from 'react-router-dom'; 2 | 3 | export default function ErrorPage() { 4 | const error: any = useRouteError(); 5 | console.error(error); 6 | 7 | return ( 8 |
9 |

Oops!

10 |

Sorry, an unexpected error has occurred.

11 |

12 | {error.statusText || error.message} 13 |

14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.component.tsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | INITIALIZE_SELECT_ACTION_ROUTE, 5 | INITIALIZE_END_OF_FLOW_ROUTE, 6 | INITIALIZE_ROUTE, 7 | } from '../../../../constants/routes'; 8 | import CreateNewVault from '../../../../components/app/create-new-vault'; 9 | import { Box } from '@mui/material'; 10 | // import { 11 | // EVENT, 12 | // EVENT_NAMES, 13 | // } from '../../../../../shared/constants/metametrics'; 14 | 15 | export default class ImportWithSeedPhrase extends PureComponent { 16 | static contextTypes = { 17 | t: PropTypes.func, 18 | trackEvent: PropTypes.func, 19 | }; 20 | 21 | // static propTypes = { 22 | // history: PropTypes.object, 23 | // onSubmit: PropTypes.func.isRequired, 24 | // setSeedPhraseBackedUp: PropTypes.func, 25 | // }; 26 | 27 | // UNSAFE_componentWillMount() { 28 | // this._onBeforeUnload = () => 29 | // this.context.trackEvent({ 30 | // category: EVENT.CATEGORIES.ONBOARDING, 31 | // event: EVENT_NAMES.WALLET_SETUP_FAILED, 32 | // properties: { 33 | // account_type: EVENT.ACCOUNT_TYPES.IMPORTED, 34 | // account_import_type: EVENT.ACCOUNT_IMPORT_TYPES.SRP, 35 | // reason: 'Seed Phrase Error', 36 | // error: this.state.seedPhraseError, 37 | // }, 38 | // }); 39 | // window.addEventListener('beforeunload', this._onBeforeUnload); 40 | // } 41 | 42 | componentWillUnmount() { 43 | // window.removeEventListener('beforeunload', this._onBeforeUnload); 44 | } 45 | 46 | handleImport = async (password: string, seedPhrase: any) => { 47 | const { history, onSubmit, setSeedPhraseBackedUp } = this.props as any; 48 | 49 | await onSubmit(password, seedPhrase); 50 | // this.context.trackEvent({ 51 | // category: EVENT.CATEGORIES.ONBOARDING, 52 | // event: EVENT_NAMES.WALLET_CREATED, 53 | // properties: { 54 | // account_type: EVENT.ACCOUNT_TYPES.IMPORTED, 55 | // account_import_type: EVENT.ACCOUNT_IMPORT_TYPES.SRP, 56 | // }, 57 | // }); 58 | 59 | await setSeedPhraseBackedUp(true); 60 | history.replace(INITIALIZE_END_OF_FLOW_ROUTE); 61 | }; 62 | 63 | render() { 64 | const { theme } = this.props as any; 65 | // const { t } = this.context; 66 | 67 | return ( 68 | 69 | 100 | 101 | ); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/create-password/import-with-seed-phrase/import-with-seed-phrase.container.tsx: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { setSeedPhraseBackedUp } from '../../../../store/actions'; 3 | import ImportWithSeedPhrase from './import-with-seed-phrase.component'; 4 | import { withParamsAndNavigate } from '~/components/with/WithNavigate'; 5 | import { withTheme } from '~/components/with/WithTheme'; 6 | 7 | const mapDispatchToProps = (dispatch: any) => { 8 | return { 9 | setSeedPhraseBackedUp: (seedPhraseBackupState: any) => 10 | dispatch(setSeedPhraseBackedUp(seedPhraseBackupState)), 11 | }; 12 | }; 13 | 14 | export default withTheme( 15 | withParamsAndNavigate(connect(null, mapDispatchToProps)(ImportWithSeedPhrase)), 16 | ); 17 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/create-password/import-with-seed-phrase/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './import-with-seed-phrase.container'; 2 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/create-password/new-account/index.tsx: -------------------------------------------------------------------------------- 1 | import { withParamsAndNavigate } from '~/components/with/WithNavigate'; 2 | import NewAccount from './new-account.component'; 3 | import { withTheme } from '~/components/with/WithTheme'; 4 | import { withDispatch } from '~/components/with/WithDispatch'; 5 | 6 | export default withTheme(withParamsAndNavigate(withDispatch(NewAccount))); 7 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/create-password/new-account/new-account.test.js: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | // import { fireEvent, waitFor } from '@testing-library/react'; 3 | // import { 4 | // INITIALIZE_SEED_PHRASE_INTRO_ROUTE, 5 | // INITIALIZE_SELECT_ACTION_ROUTE, 6 | // } from '../../../../helpers/constants/routes'; 7 | // import { renderWithProvider } from '../../../../../test/lib/render-helpers'; 8 | // import NewAccount from '.'; 9 | 10 | // describe('Name of the group', () => { 11 | // const props = { 12 | // onSubmit: jest.fn(), 13 | // history: { 14 | // push: jest.fn(), 15 | // }, 16 | // }; 17 | 18 | // it('should match snapshot', () => { 19 | // const { container } = renderWithProvider(); 20 | // expect(container).toMatchSnapshot(); 21 | // }); 22 | 23 | // it('should back button', () => { 24 | // const { queryByTestId } = renderWithProvider(); 25 | // const onboardingBackButton = queryByTestId('onboarding-back-button'); 26 | 27 | // fireEvent.click(onboardingBackButton); 28 | 29 | // expect(props.history.push).toHaveBeenCalledWith( 30 | // INITIALIZE_SELECT_ACTION_ROUTE, 31 | // ); 32 | // }); 33 | 34 | // it('should initialize with a disabled create button', () => { 35 | // const { queryByRole } = renderWithProvider(); 36 | // const createButton = queryByRole('button', { type: 'primary' }); 37 | 38 | // expect(createButton).toBeInTheDocument(); 39 | // expect(createButton).toHaveAttribute('disabled'); 40 | // }); 41 | 42 | // it('should', async () => { 43 | // const { queryByRole, queryByTestId } = renderWithProvider( 44 | // , 45 | // ); 46 | 47 | // const password = 'a-new-password'; 48 | 49 | // const checkTerms = queryByRole('checkbox'); 50 | 51 | // const createPassword = queryByTestId('create-password'); 52 | // const confirmPassword = queryByTestId('confirm-password'); 53 | 54 | // const createButton = queryByRole('button', { type: 'primary' }); 55 | // fireEvent.click(checkTerms); 56 | // fireEvent.change(createPassword, { target: { value: password } }); 57 | // fireEvent.change(confirmPassword, { target: { value: password } }); 58 | 59 | // expect(createButton).not.toHaveAttribute('disabled'); 60 | 61 | // fireEvent.click(createButton); 62 | 63 | // await waitFor(() => { 64 | // expect(props.history.push).toHaveBeenCalledWith( 65 | // INITIALIZE_SEED_PHRASE_INTRO_ROUTE, 66 | // ); 67 | // }); 68 | // }); 69 | 70 | // it('should error when the password and the confirm password are not the same', () => { 71 | // const { queryByRole, queryByTestId, queryByText } = renderWithProvider( 72 | // , 73 | // ); 74 | 75 | // const password = 'a-new-password'; 76 | // const wrongPassword = 'wrong-password'; 77 | 78 | // const createPassword = queryByTestId('create-password'); 79 | // const confirmPassword = queryByTestId('confirm-password'); 80 | 81 | // fireEvent.change(createPassword, { target: { value: password } }); 82 | // fireEvent.change(confirmPassword, { target: { value: wrongPassword } }); 83 | 84 | // const errorMessage = queryByText(/passwordsDontMatch/u); 85 | // expect(errorMessage).toBeInTheDocument(); 86 | 87 | // // Create button is disabled. 88 | // const createButton = queryByRole('button', { type: 'primary' }); 89 | // expect(createButton).toHaveAttribute('disabled'); 90 | // }); 91 | 92 | // it('should disable the create button if the terms are not checked but passwords are correct', () => { 93 | // const { queryByRole, queryByTestId } = renderWithProvider( 94 | // , 95 | // ); 96 | 97 | // const password = 'a-new-password'; 98 | 99 | // const createPassword = queryByTestId('create-password'); 100 | // const confirmPassword = queryByTestId('confirm-password'); 101 | 102 | // fireEvent.change(createPassword, { target: { value: password } }); 103 | // fireEvent.change(confirmPassword, { target: { value: password } }); 104 | 105 | // const createButton = queryByRole('button', { type: 'primary' }); 106 | // expect(createButton).toHaveAttribute('disabled'); 107 | // }); 108 | // }); 109 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/end-of-flow/end-of-flow.component.tsx: -------------------------------------------------------------------------------- 1 | import React, { PureComponent } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | // import Snackbar from '../../../components/ui/snackbar'; 4 | // import MetaFoxLogo from '../../../components/ui/metafox-logo'; 5 | // import { SUPPORT_REQUEST_LINK } from '../../../constants/common'; 6 | import { DEFAULT_ROUTE } from '../../../constants/routes'; 7 | // import { returnToOnboardingInitiatorTab } from '../onboarding-initiator-util'; 8 | // import { 9 | // EVENT, 10 | // EVENT_NAMES, 11 | // CONTEXT_PROPS, 12 | // } from '../../../../shared/constants/metametrics'; 13 | // import ZENDESK_URLS from '../../../helpers/constants/zendesk-url'; 14 | import { t } from '~/utils/helper'; 15 | import { Box, Button } from '@mui/material'; 16 | 17 | export default class EndOfFlowScreen extends PureComponent { 18 | static contextTypes = { 19 | t: PropTypes.func, 20 | trackEvent: PropTypes.func, 21 | setOnBoardedInThisUISession: PropTypes.func, 22 | }; 23 | 24 | // static propTypes = { 25 | // history: PropTypes.object, 26 | // setCompletedOnboarding: PropTypes.func, 27 | // onboardingInitiator: PropTypes.exact({ 28 | // location: PropTypes.string, 29 | // tabId: PropTypes.number, 30 | // }), 31 | // setOnBoardedInThisUISession: PropTypes.func, 32 | // }; 33 | 34 | async _beforeUnload() { 35 | await this._onOnboardingComplete(); 36 | } 37 | 38 | _removeBeforeUnload() { 39 | window.removeEventListener('beforeunload', this._beforeUnload); 40 | } 41 | 42 | async _onOnboardingComplete() { 43 | const { setCompletedOnboarding, setOnBoardedInThisUISession } = this.props as any; 44 | // setOnBoardedInThisUISession(true); 45 | await setCompletedOnboarding(); 46 | } 47 | 48 | onComplete = async () => { 49 | const { navigate, onboardingInitiator } = this.props as any; 50 | 51 | this._removeBeforeUnload(); 52 | await this._onOnboardingComplete(); 53 | if (onboardingInitiator) { 54 | // await returnToOnboardingInitiatorTab(onboardingInitiator); 55 | } 56 | navigate(DEFAULT_ROUTE); 57 | }; 58 | 59 | componentDidMount() { 60 | window.addEventListener('beforeunload', this._beforeUnload.bind(this)); 61 | } 62 | 63 | componentWillUnmount = () => { 64 | this._removeBeforeUnload(); 65 | }; 66 | 67 | render() { 68 | const { onboardingInitiator } = this.props as any; 69 | 70 | return ( 71 | 72 |
73 |
🎉
74 |
{t('congratulations')}
75 |
76 | {t('endOfFlowMessage1')} 77 |
78 |
79 | {t('endOfFlowMessage2')} 80 |
81 |
{`• ${t('endOfFlowMessage3')}`}
82 |
{`• ${t('endOfFlowMessage4')}`}
83 |
{`• ${t('endOfFlowMessage5')}`}
84 |
{`• ${t('endOfFlowMessage6')}`}
85 |
86 | {`*${t('endOfFlowMessage8')}`}  87 |
88 | 96 | {/* {onboardingInitiator ? ( 97 | 103 | ) : null} */} 104 |
105 |
106 | ); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/end-of-flow/end-of-flow.container.tsx: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | 3 | import { getOnboardingInitiator } from '../../../selectors'; 4 | import { setCompletedOnboarding } from '../../../store/actions'; 5 | import { setOnBoardedInThisUISession } from '../../../ducks/app/app'; 6 | import EndOfFlow from './end-of-flow.component'; 7 | 8 | const mapStateToProps = (state: any) => { 9 | return { 10 | onboardingInitiator: getOnboardingInitiator(state), 11 | }; 12 | }; 13 | 14 | const mapDispatchToProps = (dispatch: any) => { 15 | return { 16 | setCompletedOnboarding: () => dispatch(setCompletedOnboarding()), 17 | setOnBoardedInThisUISession: (value: any) => dispatch(setOnBoardedInThisUISession(value)), 18 | }; 19 | }; 20 | 21 | export default connect(mapStateToProps, mapDispatchToProps)(EndOfFlow); 22 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/end-of-flow/end-of-flow.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import sinon from 'sinon'; 3 | import { fireEvent, screen } from '@testing-library/react'; 4 | import { tick } from '../../../../test/lib/tick'; 5 | import { renderWithProvider } from '../../../../test/lib/render-helpers'; 6 | import { DEFAULT_ROUTE } from '../../../helpers/constants/routes'; 7 | import EndOfFlowScreen from './end-of-flow.container'; 8 | 9 | describe('End of Flow Screen', () => { 10 | const props = { 11 | history: { 12 | push: sinon.stub(), 13 | }, 14 | setCompletedOnboarding: sinon.stub().resolves(), 15 | setOnBoardedInThisUISession: sinon.stub(), 16 | }; 17 | 18 | beforeEach(() => { 19 | renderWithProvider(); 20 | }); 21 | 22 | it('should render', () => { 23 | const endOfFlow = screen.queryByTestId('end-of-flow'); 24 | expect(endOfFlow).toBeInTheDocument(); 25 | }); 26 | 27 | it('should navigate to the default route on click', async () => { 28 | const endOfFlowButton = screen.getByTestId('EOF-complete-button'); 29 | fireEvent.click(endOfFlowButton); 30 | 31 | await tick(); 32 | 33 | expect( 34 | props.history.push.calledOnceWithExactly(DEFAULT_ROUTE), 35 | ).toStrictEqual(true); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/end-of-flow/index.scss: -------------------------------------------------------------------------------- 1 | .end-of-flow { 2 | color: var(--color-text-default); 3 | font-style: normal; 4 | 5 | .app-header__logo-container { 6 | width: 742px; 7 | margin-top: 3%; 8 | 9 | @include screen-sm-max { 10 | width: 100%; 11 | } 12 | } 13 | 14 | &__text-1, 15 | &__text-3 { 16 | @include Paragraph; 17 | 18 | font-weight: normal; 19 | margin-top: 18px; 20 | } 21 | 22 | &__text-2 { 23 | @include Paragraph; 24 | 25 | font-weight: bold; 26 | margin-top: 26px; 27 | } 28 | 29 | &__text-3 { 30 | margin-top: 2px; 31 | margin-bottom: 2px; 32 | 33 | @include screen-sm-max { 34 | @include H6; 35 | 36 | margin-bottom: 16px; 37 | } 38 | } 39 | 40 | &__text-4 { 41 | margin-top: 26px; 42 | } 43 | 44 | button { 45 | width: 207px; 46 | } 47 | 48 | &__start-over-button { 49 | width: 744px; 50 | } 51 | 52 | &__emoji { 53 | font-size: 80px; 54 | margin-top: 70px; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/end-of-flow/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './end-of-flow.container'; 2 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/confirm-seed-phrase/ItemTypes.ts: -------------------------------------------------------------------------------- 1 | export const ItemTypes = { 2 | CARD: 'card', 3 | }; 4 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/confirm-seed-phrase/confirm-seed-phrase.container.tsx: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { setSeedPhraseBackedUp } from '../../../../store/actions'; 3 | import ConfirmSeedPhrase from './confirm-seed-phrase.component'; 4 | 5 | const mapStateToProps = (state: any) => { 6 | console.log('state:', state); 7 | return { 8 | seedPhrase: state.wallet.seed, 9 | }; 10 | }; 11 | 12 | const mapDispatchToProps = (dispatch: any) => { 13 | return { 14 | setSeedPhraseBackedUp: (seedPhraseBackupState: any) => 15 | dispatch(setSeedPhraseBackedUp(seedPhraseBackupState)), 16 | }; 17 | }; 18 | 19 | export default connect(mapStateToProps, mapDispatchToProps)(ConfirmSeedPhrase); 20 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/confirm-seed-phrase/draggable-seed.component.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | import { useDrag, useDrop } from 'react-dnd'; 3 | import { ItemTypes } from './ItemTypes.js'; 4 | import classNames from 'classnames'; 5 | const style = { 6 | cursor: 'move', 7 | }; 8 | export const DraggableSeed = ({ 9 | id, 10 | word, 11 | index, 12 | moveCard, 13 | className = '', 14 | selected, 15 | isOver, 16 | canDrop, 17 | }: any) => { 18 | const ref = useRef(null); 19 | const [{ handlerId }, drop] = useDrop({ 20 | accept: ItemTypes.CARD, 21 | collect(monitor) { 22 | return { 23 | handlerId: monitor.getHandlerId(), 24 | }; 25 | }, 26 | hover(item: any, monitor: any) { 27 | if (!ref.current) { 28 | return; 29 | } 30 | const dragIndex = item.index; 31 | const hoverIndex = index; 32 | // Don't replace items with themselves 33 | if (dragIndex === hoverIndex) { 34 | return; 35 | } 36 | // Determine rectangle on screen 37 | //@ts-ignore 38 | const hoverBoundingRect = ref.current?.getBoundingClientRect(); 39 | // Get vertical middle 40 | const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2; 41 | // Determine mouse position 42 | const clientOffset = monitor.getClientOffset(); 43 | // Get pixels to the top 44 | const hoverClientY = clientOffset.y - hoverBoundingRect.top; 45 | // Only perform the move when the mouse has crossed half of the items height 46 | // When dragging downwards, only move when the cursor is below 50% 47 | // When dragging upwards, only move when the cursor is above 50% 48 | // Dragging downwards 49 | if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { 50 | return; 51 | } 52 | // Dragging upwards 53 | if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { 54 | return; 55 | } 56 | // Time to actually perform the action 57 | moveCard(dragIndex, hoverIndex); 58 | // Note: we're mutating the monitor item here! 59 | // Generally it's better to avoid mutations, 60 | // but it's good here for the sake of performance 61 | // to avoid expensive index searches. 62 | item.index = hoverIndex; 63 | }, 64 | }); 65 | const [{ isDragging }, drag] = useDrag({ 66 | type: ItemTypes.CARD, 67 | item: () => { 68 | return { id, index }; 69 | }, 70 | collect: (monitor) => ({ 71 | isDragging: monitor.isDragging(), 72 | }), 73 | }); 74 | const opacity = isDragging ? 0 : 1; 75 | drag(drop(ref)); 76 | return ( 77 |
89 | {word} 90 |
91 | ); 92 | }; 93 | 94 | export default DraggableSeed; 95 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './confirm-seed-phrase.container'; 2 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.scss: -------------------------------------------------------------------------------- 1 | .confirm-seed-phrase { 2 | &__back-button { 3 | margin-bottom: 12px; 4 | } 5 | 6 | &__sorted-seed-words { 7 | max-width: $screen-sm-max; 8 | } 9 | 10 | &__seed-word { 11 | display: inline-flex; 12 | flex-flow: row nowrap; 13 | align-items: center; 14 | justify-content: center; 15 | padding: 8px 18px; 16 | width: 128px; 17 | height: 41px; 18 | margin: 4px; 19 | text-align: center; 20 | border-radius: 4px; 21 | cursor: move; 22 | 23 | &--sorted { 24 | cursor: pointer; 25 | margin: 6px; 26 | } 27 | 28 | &--selected { 29 | color: var(--color-overlay-inverse); 30 | } 31 | 32 | &--dragging { 33 | margin: 0; 34 | } 35 | 36 | &--empty { 37 | background-color: transparent; 38 | border-color: transparent; 39 | cursor: default; 40 | 41 | &:hover, 42 | &:active { 43 | background-color: transparent; 44 | border-color: transparent; 45 | cursor: default; 46 | box-shadow: none !important; 47 | } 48 | } 49 | 50 | &--hidden { 51 | display: none !important; 52 | } 53 | 54 | &--drop-hover { 55 | background-color: transparent; 56 | border-color: transparent; 57 | color: transparent; 58 | } 59 | 60 | @include screen-sm-max { 61 | @include H6; 62 | 63 | padding: 6px 18px; 64 | } 65 | } 66 | 67 | &__selected-seed-words { 68 | /*rtl:ignore*/ 69 | direction: ltr; 70 | display: flex; 71 | flex-flow: row wrap; 72 | min-height: 161px; 73 | max-width: $screen-sm-max; 74 | border: 1px solid var(--color-border-muted); 75 | border-radius: 6px; 76 | background-color: var(--color-background-default); 77 | margin: 24px 0 36px; 78 | padding: 12px; 79 | 80 | &__pending-seed { 81 | display: none; 82 | } 83 | 84 | &__selected-seed { 85 | display: inline-flex; 86 | 87 | &:hover { 88 | box-shadow: var(--shadow-size-xs) var(--color-shadow-default); 89 | } 90 | } 91 | 92 | &--dragging { 93 | .confirm-seed-phrase__selected-seed-words__pending-seed { 94 | display: inline-flex; 95 | } 96 | 97 | .confirm-seed-phrase__selected-seed-words__selected-seed { 98 | display: none; 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/index.scss: -------------------------------------------------------------------------------- 1 | // @import 'confirm-seed-phrase/index'; 2 | @import 'reveal-seed-phrase/index'; 3 | // @import 'seed-phrase-intro/index'; 4 | 5 | .seed-phrase { 6 | &__sections { 7 | display: flex; 8 | 9 | @include screen-sm-min { 10 | flex-direction: column; 11 | } 12 | 13 | @include screen-sm-max { 14 | flex-direction: column; 15 | } 16 | } 17 | 18 | &__main { 19 | flex: 3; 20 | min-width: 0; 21 | } 22 | 23 | &__side { 24 | flex: 2; 25 | min-width: 0; 26 | 27 | @include screen-sm-min { 28 | // margin-left: 81px; 29 | margin-top: 24px; 30 | } 31 | 32 | @include screen-sm-max { 33 | margin-top: 24px; 34 | } 35 | 36 | .first-time-flow__text-block { 37 | color: var(--color-text-default); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.scss: -------------------------------------------------------------------------------- 1 | .reveal-seed-phrase { 2 | @include screen-sm-max { 3 | display: flex; 4 | flex-direction: column; 5 | width: 96%; 6 | margin-left: 2%; 7 | margin-right: 2%; 8 | } 9 | 10 | &__secret { 11 | position: relative; 12 | display: flex; 13 | justify-content: center; 14 | border: 1px solid var(--color-border-default); 15 | border-radius: 6px; 16 | background-color: var(--color-background-default); 17 | padding: 18px; 18 | margin-top: 36px; 19 | max-width: 350px; 20 | } 21 | 22 | &__secret-words { 23 | @include H4; 24 | 25 | width: 310px; 26 | text-align: center; 27 | 28 | &--hidden { 29 | filter: blur(5px); 30 | } 31 | } 32 | 33 | &__secret-blocker { 34 | position: absolute; 35 | top: 0; 36 | bottom: 0; 37 | height: 100%; 38 | width: 100%; 39 | background-color: var(--color-overlay-default); 40 | display: flex; 41 | flex-flow: column nowrap; 42 | align-items: center; 43 | justify-content: center; 44 | padding: 8px 0 18px; 45 | cursor: pointer; 46 | } 47 | 48 | &__reveal-button { 49 | @include H7; 50 | 51 | color: var(--color-overlay-inverse); 52 | font-weight: 500; 53 | text-transform: uppercase; 54 | margin-top: 8px; 55 | text-align: center; 56 | } 57 | 58 | &__export-text { 59 | color: var(--color-primary-default); 60 | cursor: pointer; 61 | font-weight: 500; 62 | } 63 | 64 | button { 65 | margin-top: 0; 66 | } 67 | 68 | &__buttons { 69 | display: flex; 70 | margin-top: 10px; 71 | 72 | .first-time-flow__button:last-of-type { 73 | margin-left: 10px; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/reveal-seed-phrase/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './reveal-seed-phrase.container'; 2 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.container.tsx: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { setCompletedOnboarding, setSeedPhraseBackedUp } from '../../../../store/actions'; 3 | import { getOnboardingInitiator } from '../../../../selectors'; 4 | import RevealSeedPhrase from './reveal-seed-phrase.component'; 5 | import { withParamsAndNavigate } from '~/components/with/WithNavigate'; 6 | 7 | const mapStateToProps = (state: any) => { 8 | console.log('state:', state); 9 | return { 10 | onboardingInitiator: getOnboardingInitiator(state), 11 | seedPhrase: state.wallet.seed, 12 | }; 13 | }; 14 | 15 | const mapDispatchToProps = (dispatch: any) => { 16 | return { 17 | setSeedPhraseBackedUp: (seedPhraseBackupState: any) => 18 | dispatch(setSeedPhraseBackedUp(seedPhraseBackupState)), 19 | setCompletedOnboarding: () => dispatch(setCompletedOnboarding()), 20 | }; 21 | }; 22 | 23 | export default withParamsAndNavigate( 24 | connect(mapStateToProps, mapDispatchToProps)(RevealSeedPhrase), 25 | ); 26 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/seed-phrase/reveal-seed-phrase/reveal-seed-phrase.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import sinon from 'sinon'; 3 | import { fireEvent } from '@testing-library/react'; 4 | import configureMockStore from 'redux-mock-store'; 5 | import { renderWithProvider } from '../../../../../test/lib/render-helpers'; 6 | import RevealSeedPhrase from '.'; 7 | 8 | describe('Reveal Secret Recovery Phrase', () => { 9 | const TEST_SEED = 10 | 'debris dizzy just program just float decrease vacant alarm reduce speak stadium'; 11 | 12 | const props = { 13 | history: { 14 | push: sinon.spy(), 15 | }, 16 | seedPhrase: TEST_SEED, 17 | setSeedPhraseBackedUp: sinon.spy(), 18 | setCompletedOnboarding: sinon.spy(), 19 | }; 20 | 21 | const mockState = { 22 | metamask: {}, 23 | }; 24 | 25 | const mockStore = configureMockStore()(mockState); 26 | 27 | it('should match snapshot', () => { 28 | const { container } = renderWithProvider( 29 | , 30 | mockStore, 31 | ); 32 | 33 | expect(container).toMatchSnapshot(); 34 | }); 35 | 36 | it('clicks to reveal shows seed phrase', () => { 37 | const { queryByTestId } = renderWithProvider( 38 | , 39 | mockStore, 40 | ); 41 | 42 | fireEvent.click(queryByTestId('reveal-seed-blocker')); 43 | 44 | expect(queryByTestId('showing-seed-phrase')).toBeInTheDocument(); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/select-action/index.scss: -------------------------------------------------------------------------------- 1 | .select-action { 2 | .app-header__logo-container { 3 | width: 742px; 4 | margin-top: 3%; 5 | } 6 | 7 | &__body { 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | } 12 | 13 | &__body-header { 14 | // @include H3; 15 | 16 | text-align: center; 17 | margin-top: 65px; 18 | color: var(--color-text-default); 19 | } 20 | 21 | &__select-buttons { 22 | display: grid; 23 | grid-template-columns: repeat(2, 1fr); 24 | gap: 24px; 25 | margin-top: 40px; 26 | } 27 | 28 | &__select-button { 29 | display: flex; 30 | flex-direction: column; 31 | align-items: center; 32 | justify-content: space-evenly; 33 | width: 388px; 34 | height: 278px; 35 | border: 1px solid var(--color-border-muted); 36 | box-sizing: border-box; 37 | border-radius: 10px; 38 | padding: 8px; 39 | 40 | .first-time-flow__button { 41 | max-width: 221px; 42 | height: 44px; 43 | font-size: large; 44 | } 45 | } 46 | 47 | &__button-symbol { 48 | color: var(--color-icon-muted); 49 | margin-top: 41px; 50 | } 51 | 52 | &__button-content { 53 | display: flex; 54 | flex-direction: column; 55 | justify-content: center; 56 | align-items: center; 57 | height: 144px; 58 | } 59 | 60 | &__button-text-big { 61 | // @include H4; 62 | 63 | color: var(--color-text-default); 64 | margin-top: 12px; 65 | text-align: center; 66 | } 67 | 68 | &__button-text-small { 69 | // @include H6; 70 | 71 | color: var(--color-text-alternative); 72 | margin-top: 10px; 73 | text-align: center; 74 | } 75 | 76 | button { 77 | font-weight: 500; 78 | width: 221px; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/select-action/index.tsx: -------------------------------------------------------------------------------- 1 | export { default } from './select-action.container'; 2 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/select-action/select-action.container.tsx: -------------------------------------------------------------------------------- 1 | import { connect } from 'react-redux'; 2 | import { compose } from 'redux'; 3 | import { setFirstTimeFlowType } from '../../../store/actions'; 4 | import { getFirstTimeFlowTypeRoute } from '../../../selectors'; 5 | import Welcome from './select-action.component'; 6 | import { withParamsAndNavigate } from '~/components/with/WithNavigate'; 7 | 8 | const mapStateToProps = (state: any) => { 9 | return { 10 | nextRoute: getFirstTimeFlowTypeRoute(state), 11 | metaMetricsId: state.wallet.metaMetricsId, 12 | }; 13 | }; 14 | 15 | const mapDispatchToProps = (dispatch: any) => { 16 | return { 17 | setFirstTimeFlowType: (type: any) => dispatch(setFirstTimeFlowType(type)), 18 | }; 19 | }; 20 | 21 | export default withParamsAndNavigate( 22 | compose(connect(mapStateToProps, mapDispatchToProps))(Welcome), 23 | ); 24 | -------------------------------------------------------------------------------- /src/pages/first-time-flow/select-action/select-action.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import sinon from 'sinon'; 3 | import { fireEvent, screen } from '@testing-library/react'; 4 | import { renderWithProvider } from '../../../../test/lib/render-helpers'; 5 | import SelectAction from './select-action.container'; 6 | 7 | describe('Selection Action', () => { 8 | const props = { 9 | isInitialized: false, 10 | setFirstTimeFlowType: sinon.spy(), 11 | history: { 12 | push: sinon.spy(), 13 | }, 14 | }; 15 | 16 | beforeEach(() => { 17 | renderWithProvider(); 18 | }); 19 | 20 | afterEach(() => { 21 | props.setFirstTimeFlowType.resetHistory(); 22 | props.history.push.resetHistory(); 23 | }); 24 | 25 | it('clicks import wallet to route to import FTF', () => { 26 | const importButton = screen.getByTestId('import-wallet-button'); 27 | fireEvent.click(importButton); 28 | 29 | expect(props.setFirstTimeFlowType.calledOnce).toStrictEqual(true); 30 | expect(props.setFirstTimeFlowType.getCall(0).args[0]).toStrictEqual( 31 | 'import', 32 | ); 33 | expect(props.history.push.calledOnce).toStrictEqual(true); 34 | }); 35 | 36 | it('clicks create wallet to route to create FTF', () => { 37 | const importButton = screen.getByTestId('create-wallet-button'); 38 | fireEvent.click(importButton); 39 | 40 | expect(props.setFirstTimeFlowType.calledOnce).toStrictEqual(true); 41 | expect(props.setFirstTimeFlowType.getCall(0).args[0]).toStrictEqual( 42 | 'create', 43 | ); 44 | expect(props.history.push.calledOnce).toStrictEqual(true); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/pages/pages.scss: -------------------------------------------------------------------------------- 1 | /** Please import your files in alphabetical order **/ 2 | // @import 'add-collectible/index'; 3 | // @import 'import-token/index'; 4 | // @import 'asset/asset'; 5 | // @import 'confirm-import-token/index'; 6 | // @import 'confirm-add-suggested-token/index'; 7 | // @import 'confirm-approve/index'; 8 | // @import 'confirm-decrypt-message/confirm-decrypt-message'; 9 | // @import 'confirm-encryption-public-key/confirm-encryption-public-key'; 10 | // @import 'confirm-transaction-base/transaction-alerts/transaction-alerts'; 11 | // @import 'confirmation/confirmation'; 12 | // @import 'connected-sites/index'; 13 | // @import 'connected-accounts/index'; 14 | // @import 'connected-sites/index'; 15 | // @import 'create-account/index'; 16 | // @import 'error/index'; 17 | @import 'first-time-flow/index'; 18 | // @import 'send/gas-display/index'; 19 | // @import 'home/index'; 20 | // @import 'keychains/index'; 21 | // @import 'permissions-connect/index'; 22 | // @import 'send/send'; 23 | // @import 'settings/index'; 24 | // @import 'swaps/index'; 25 | // @import 'token-allowance/index'; 26 | // @import 'token-details/index'; 27 | // @import 'unlock-page/index'; 28 | // @import 'onboarding-flow/index'; 29 | // @import 'notifications/index'; 30 | -------------------------------------------------------------------------------- /src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /src/scripts/content-script.ts: -------------------------------------------------------------------------------- 1 | import browser from 'webextension-polyfill'; 2 | import {} from 'src/scripts/ui'; 3 | console.log('content-script'); 4 | // let count = 1; 5 | // count++; 6 | 7 | // document.body.style.background = '#09a33a'; 8 | 9 | // chrome.notifications.create({ 10 | // type: 'basic', 11 | // iconUrl: browser.runtime.getURL('icons/cake-96.png'), 12 | // title: 'Time for cake!', 13 | // message: 'Something something cake', 14 | // }); 15 | 16 | // const button = document.createElement('button'); 17 | // button.addEventListener('click', () => { 18 | // // chrome.tabs.query({ active: true, lastFocusedWindow: true }, function (tabs) { 19 | // // tabURL = tabs[0].id; 20 | // // browser.runtime.sendMessage(tabURL, 'OpenPopup'); 21 | // // }); 22 | // }); 23 | 24 | // document.body.appendChild(button); 25 | 26 | chrome.runtime.onMessage.addListener((request) => { 27 | console.log('rcvd1'); 28 | if (request == 'OpenPopup') { 29 | chrome.windows.create( 30 | { 31 | url: `index.html`, 32 | type: `popup`, 33 | focused: true, 34 | width: 400, 35 | height: 600, 36 | top: 0, 37 | }, 38 | () => {}, 39 | ); 40 | } 41 | }); 42 | 43 | // setInterval(() => { 44 | // chrome.runtime.sendMessage('keep alive', function (response) { 45 | // console.log(response); 46 | // }); 47 | // }, 5000); 48 | 49 | chrome.runtime.sendMessage('UI start1'); 50 | browser.runtime.sendMessage('UI start'); 51 | -------------------------------------------------------------------------------- /src/scripts/controllers/onboarding.ts: -------------------------------------------------------------------------------- 1 | // import { ObservableStore } from '@metamask/obs-store'; 2 | import log from 'loglevel'; 3 | // const ObservableStore = require('obs-store'); 4 | 5 | /** 6 | * @typedef {object} InitState 7 | * @property {boolean} seedPhraseBackedUp Indicates whether the user has completed the seed phrase backup challenge 8 | * @property {boolean} completedOnboarding Indicates whether the user has completed the onboarding flow 9 | */ 10 | 11 | /** 12 | * @typedef {object} OnboardingOptions 13 | * @property {InitState} initState The initial controller state 14 | */ 15 | 16 | /** 17 | * Controller responsible for maintaining 18 | * state related to onboarding 19 | */ 20 | export default class OnboardingController { 21 | store?: any; 22 | /** 23 | * Creates a new controller instance 24 | * 25 | * @param {OnboardingOptions} [opts] - Controller configuration parameters 26 | */ 27 | constructor(opts: any = {}) { 28 | const initialTransientState = { 29 | onboardingTabs: {}, 30 | }; 31 | const initState = { 32 | seedPhraseBackedUp: null, 33 | firstTimeFlowType: null, 34 | completedOnboarding: false, 35 | ...opts.initState, 36 | ...initialTransientState, 37 | }; 38 | // this.store = new ObservableStore({}); 39 | } 40 | 41 | setSeedPhraseBackedUp(newSeedPhraseBackUpState: any) { 42 | this.store?.updateState({ 43 | seedPhraseBackedUp: newSeedPhraseBackUpState, 44 | }); 45 | } 46 | 47 | // /** 48 | // * Sets the completedOnboarding state to true, indicating that the user has completed the 49 | // * onboarding process. 50 | // */ 51 | completeOnboarding() { 52 | this.store?.updateState({ 53 | completedOnboarding: true, 54 | }); 55 | return Promise.resolve(true); 56 | } 57 | 58 | /** 59 | * Setter for the `firstTimeFlowType` property 60 | * 61 | * @param {string} type - Indicates the type of first time flow - create or import - the user wishes to follow 62 | */ 63 | setFirstTimeFlowType(type: any) { 64 | this.store?.updateState({ firstTimeFlowType: type }); 65 | } 66 | 67 | /** 68 | * Registering a site as having initiated onboarding 69 | * 70 | * @param {string} location - The location of the site registering 71 | * @param {string} tabId - The id of the tab registering 72 | */ 73 | registerOnboarding = async (location: any, tabId: any) => { 74 | if (this.store?.getState().completedOnboarding) { 75 | log.debug('Ignoring registerOnboarding; user already onboarded'); 76 | return; 77 | } 78 | const onboardingTabs = { ...this.store?.getState().onboardingTabs }; 79 | if (!onboardingTabs[location] || onboardingTabs[location] !== tabId) { 80 | log.debug(`Registering onboarding tab at location '${location}' with tabId '${tabId}'`); 81 | onboardingTabs[location] = tabId; 82 | this.store?.updateState({ onboardingTabs }); 83 | } 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /src/scripts/lib/metaRPCClientFactory.ts: -------------------------------------------------------------------------------- 1 | import { EthereumRpcError } from 'eth-rpc-errors'; 2 | // import SafeEventEmitter from 'safe-event-emitter'; 3 | import createRandomId from '../../shared/modules/random-id'; 4 | // import { TEN_SECONDS_IN_MILLISECONDS } from '../../shared/lib/transactions-controller-utils'; 5 | const TEN_SECONDS_IN_MILLISECONDS = 10000; 6 | 7 | class DisconnectError extends Error {} 8 | 9 | class MetaRPCClient { 10 | connectionStream: any; 11 | notificationChannel: any; 12 | uncaughtErrorChannel: any; 13 | requests: any; 14 | responseHandled: any; 15 | DisconnectError: any; 16 | constructor(connectionStream: any) { 17 | this.connectionStream = connectionStream; 18 | // this.notificationChannel = new SafeEventEmitter(); 19 | // this.uncaughtErrorChannel = new SafeEventEmitter(); 20 | this.requests = new Map(); 21 | this.connectionStream.on('data', this.handleResponse.bind(this)); 22 | this.connectionStream.on('end', this.close.bind(this)); 23 | this.responseHandled = {}; 24 | this.DisconnectError = DisconnectError; 25 | } 26 | 27 | send(id: string | number, payload: any, cb: any) { 28 | this.requests.set(id, cb); 29 | this.connectionStream.write(payload); 30 | this.responseHandled[id] = false; 31 | if (payload.method === 'getState') { 32 | setTimeout(() => { 33 | if (!this.responseHandled[id] && cb) { 34 | delete this.responseHandled[id]; 35 | return cb(new Error('No response from RPC'), null); 36 | } 37 | 38 | delete this.responseHandled[id]; 39 | // needed for linter to pass 40 | return true; 41 | }, TEN_SECONDS_IN_MILLISECONDS); 42 | } 43 | } 44 | 45 | onNotification(handler: any) { 46 | this.notificationChannel.addListener('notification', (data: any) => { 47 | handler(data); 48 | }); 49 | } 50 | 51 | onUncaughtError(handler: any) { 52 | this.uncaughtErrorChannel.addListener('error', (error: any) => { 53 | handler(error); 54 | }); 55 | } 56 | 57 | close() { 58 | this.notificationChannel.removeAllListeners(); 59 | this.uncaughtErrorChannel.removeAllListeners(); 60 | // fail all unfinished requests 61 | for (const [id, handler] of this.requests) { 62 | if (!this.responseHandled[id]) { 63 | this.responseHandled[id] = true; 64 | handler(new DisconnectError('disconnected')); 65 | } 66 | } 67 | } 68 | 69 | handleResponse(data: any) { 70 | const { id, result, error, method, params } = data; 71 | const isNotification = id === undefined && error === undefined; 72 | const cb = this.requests.get(id); 73 | 74 | this.responseHandled[id] = true; 75 | 76 | if (method && params && !isNotification) { 77 | // dont handle server-side to client-side requests 78 | return; 79 | } 80 | if (method && params && isNotification) { 81 | // handle servier-side to client-side notification 82 | this.notificationChannel.emit('notification', data); 83 | return; 84 | } 85 | 86 | if (error) { 87 | const e = new EthereumRpcError(error.code, error.message, error.data); 88 | // preserve the stack from serializeError 89 | e.stack = error.stack; 90 | if (cb) { 91 | this.requests.delete(id); 92 | cb(e); 93 | return; 94 | } 95 | this.uncaughtErrorChannel.emit('error', e); 96 | return; 97 | } 98 | 99 | if (!cb) { 100 | // not found in request list 101 | return; 102 | } 103 | 104 | this.requests.delete(id); 105 | 106 | cb(null, result); 107 | } 108 | } 109 | 110 | const metaRPCClientFactory = (connectionStream: any) => { 111 | const metaRPCClient = new MetaRPCClient(connectionStream); 112 | return new Proxy(metaRPCClient, { 113 | get: (object, property) => { 114 | //@ts-ignore 115 | if (object[property]) { 116 | //@ts-ignore 117 | return object[property]; 118 | } 119 | return (...p: any) => { 120 | const cb = p[p.length - 1]; 121 | const params = p.slice(0, -1); 122 | const id = createRandomId(); 123 | const payload = { 124 | jsonrpc: '2.0', 125 | method: property, 126 | params, 127 | id, 128 | }; 129 | object.send(id, payload, cb); 130 | }; 131 | }, 132 | }); 133 | }; 134 | 135 | export default metaRPCClientFactory; 136 | -------------------------------------------------------------------------------- /src/scripts/lib/seed-phrase-verifier.ts: -------------------------------------------------------------------------------- 1 | // import KeyringController from '@metamask/eth-keyring-controller'; 2 | import log from 'loglevel'; 3 | 4 | import { KEYRING_TYPES } from '../../shared/constants/keyrings'; 5 | const { KeyringController } = require('@metamask/eth-keyring-controller'); 6 | 7 | const seedPhraseVerifier = { 8 | /** 9 | * Verifies if the seed words can restore the accounts. 10 | * 11 | * Key notes: 12 | * - The seed words can recreate the primary keyring and the accounts belonging to it. 13 | * - The created accounts in the primary keyring are always the same. 14 | * - The keyring always creates the accounts in the same sequence. 15 | * 16 | * @param {Array} createdAccounts - The accounts to restore 17 | * @param {Buffer} seedPhrase - The seed words to verify, encoded as a Buffer 18 | * @returns {Promise} 19 | */ 20 | async verifyAccounts(createdAccounts: any, seedPhrase: any) { 21 | if (!createdAccounts || createdAccounts.length < 1) { 22 | throw new Error('No created accounts defined.'); 23 | } 24 | 25 | const keyringController = new KeyringController({}); 26 | console.log('key:', keyringController); 27 | const Keyring = keyringController.getKeyringClassForType(KEYRING_TYPES.HD_KEY_TREE); 28 | const opts = { 29 | mnemonic: seedPhrase, 30 | numberOfAccounts: createdAccounts.length, 31 | }; 32 | 33 | const keyring = new Keyring(opts); 34 | const restoredAccounts = await keyring.getAccounts(); 35 | log.debug(`Created accounts: ${JSON.stringify(createdAccounts)}`); 36 | log.debug(`Restored accounts: ${JSON.stringify(restoredAccounts)}`); 37 | 38 | if (restoredAccounts.length !== createdAccounts.length) { 39 | // this should not happen... 40 | throw new Error('Wrong number of accounts'); 41 | } 42 | 43 | for (let i = 0; i < restoredAccounts.length; i++) { 44 | if (restoredAccounts[i].toLowerCase() !== createdAccounts[i].toLowerCase()) { 45 | throw new Error( 46 | `Not identical accounts! Original: ${createdAccounts[i]}, Restored: ${restoredAccounts[i]}`, 47 | ); 48 | } 49 | } 50 | }, 51 | }; 52 | 53 | export default seedPhraseVerifier; 54 | -------------------------------------------------------------------------------- /src/scripts/lib/stream-utils.ts: -------------------------------------------------------------------------------- 1 | // import ObjectMultiplex from 'obj-multiplex'; 2 | const ObjectMultiplex = require('obj-multiplex'); 3 | import pump, { Callback, Stream } from 'pump'; 4 | 5 | import { EXTENSION_MESSAGES } from '../../shared/constants/app'; 6 | 7 | /** 8 | * Sets up stream multiplexing for the given stream 9 | * 10 | * @param {any} connectionStream - the stream to mux 11 | * @returns {stream.Stream} the multiplexed stream 12 | */ 13 | export function setupMultiplex(connectionStream: any) { 14 | const mux = new ObjectMultiplex(); 15 | /** 16 | * We are using this streams to send keep alive message between backend/ui without setting up a multiplexer 17 | * We need to tell the multiplexer to ignore them, else we get the " orphaned data for stream " warnings 18 | * https://github.com/MetaMask/object-multiplex/blob/280385401de84f57ef57054d92cfeb8361ef2680/src/ObjectMultiplex.ts#L63 19 | */ 20 | mux.ignoreStream(EXTENSION_MESSAGES.CONNECTION_READY); 21 | mux.ignoreStream('ACK_KEEP_ALIVE_MESSAGE'); 22 | mux.ignoreStream('WORKER_KEEP_ALIVE_MESSAGE'); 23 | pump(connectionStream, mux as unknown as Stream | Callback, connectionStream, (err: any) => { 24 | if (err) { 25 | console.error(err); 26 | } 27 | }); 28 | return mux; 29 | } 30 | -------------------------------------------------------------------------------- /src/scripts/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "EMASYA", 3 | "name": "EMASYA", 4 | "manifest_version": 3, 5 | "version": "1.0", 6 | "icons": { 7 | "16": "favicon-16x16.png", 8 | "32": "favicon-32x32.png", 9 | "48": "favicon-48x48.png", 10 | "128": "logo128.png" 11 | }, 12 | "background": { 13 | "service_worker": "src/scripts/background.ts" 14 | }, 15 | "content_scripts": [ 16 | { 17 | "matches": ["https://*/*"], 18 | "js": ["src/scripts/content-script.ts"] 19 | } 20 | ], 21 | "action": { 22 | "default_title": "EMASYA", 23 | "default_popup": "index.html" 24 | }, 25 | "permissions": [ 26 | "storage", 27 | "tabs", 28 | "activeTab", 29 | "alarms", 30 | "clipboardWrite", 31 | "notifications", 32 | "webRequest", 33 | "scripting" 34 | ], 35 | "host_permissions": [ 36 | "https://jgp4iec7b3.execute-api.eu-west-1.amazonaws.com/default/*", 37 | "https://api.tatum.io/*" 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /src/scripts/platforms/extension.test.js: -------------------------------------------------------------------------------- 1 | import browser from 'webextension-polyfill'; 2 | import ExtensionPlatform from './extension'; 3 | 4 | jest.mock('webextension-polyfill', () => { 5 | return { 6 | runtime: { 7 | getManifest: jest.fn(), 8 | }, 9 | }; 10 | }); 11 | 12 | describe('extension platform', () => { 13 | beforeEach(() => { 14 | // TODO: Delete this an enable 'resetMocks' in `jest.config.js` instead 15 | jest.resetAllMocks(); 16 | }); 17 | 18 | describe('getVersion', () => { 19 | it('should return non-prerelease version', () => { 20 | browser.runtime.getManifest.mockReturnValue({ version: '1.2.3' }); 21 | const extensionPlatform = new ExtensionPlatform(); 22 | 23 | const version = extensionPlatform.getVersion(); 24 | 25 | expect(version).toBe('1.2.3'); 26 | }); 27 | 28 | it('should return rollback version', () => { 29 | browser.runtime.getManifest.mockReturnValue({ version: '1.2.3.1' }); 30 | const extensionPlatform = new ExtensionPlatform(); 31 | 32 | const version = extensionPlatform.getVersion(); 33 | 34 | expect(version).toBe('1.2.3.1'); 35 | }); 36 | 37 | it('should return SemVer-formatted version for Chrome style manifest of prerelease', () => { 38 | browser.runtime.getManifest.mockReturnValue({ 39 | version: '1.2.3.0', 40 | version_name: '1.2.3-beta.0', 41 | }); 42 | const extensionPlatform = new ExtensionPlatform(); 43 | 44 | const version = extensionPlatform.getVersion(); 45 | 46 | expect(version).toBe('1.2.3-beta.0'); 47 | }); 48 | 49 | it('should return SemVer-formatted version for Firefox style manifest of prerelease', () => { 50 | browser.runtime.getManifest.mockReturnValue({ 51 | version: '1.2.3beta0', 52 | }); 53 | const extensionPlatform = new ExtensionPlatform(); 54 | 55 | const version = extensionPlatform.getVersion(); 56 | 57 | expect(version).toBe('1.2.3-beta.0'); 58 | }); 59 | 60 | it('should throw error if build version is missing from Chrome style prerelease manifest', () => { 61 | browser.runtime.getManifest.mockReturnValue({ 62 | version: '1.2.3', 63 | version_name: '1.2.3-beta.0', 64 | }); 65 | const extensionPlatform = new ExtensionPlatform(); 66 | 67 | expect(() => extensionPlatform.getVersion()).toThrow( 68 | 'Version missing build number:', 69 | ); 70 | }); 71 | 72 | it('should throw error if build version is missing from Firefox style prerelease manifest', () => { 73 | browser.runtime.getManifest.mockReturnValue({ 74 | version: '1.2.3beta', 75 | }); 76 | const extensionPlatform = new ExtensionPlatform(); 77 | 78 | expect(() => extensionPlatform.getVersion()).toThrow( 79 | 'Version contains invalid prerelease:', 80 | ); 81 | }); 82 | 83 | it('should throw error if patch is missing from Firefox style prerelease manifest', () => { 84 | browser.runtime.getManifest.mockReturnValue({ 85 | version: '1.2.beta0', 86 | }); 87 | const extensionPlatform = new ExtensionPlatform(); 88 | 89 | expect(() => extensionPlatform.getVersion()).toThrow( 90 | 'Version contains invalid prerelease:', 91 | ); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /src/selectors/first-time-flow.ts: -------------------------------------------------------------------------------- 1 | import { 2 | INITIALIZE_CREATE_PASSWORD_ROUTE, 3 | INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE, 4 | DEFAULT_ROUTE, 5 | ONBOARDING_CREATE_PASSWORD_ROUTE, 6 | ONBOARDING_IMPORT_WITH_SRP_ROUTE, 7 | } from '../constants/routes'; 8 | 9 | export function getFirstTimeFlowTypeRoute(state: any) { 10 | const { firstTimeFlowType } = state.wallet; 11 | 12 | let nextRoute; 13 | if (firstTimeFlowType === 'create') { 14 | nextRoute = process.env.ONBOARDING_V2 15 | ? ONBOARDING_CREATE_PASSWORD_ROUTE 16 | : INITIALIZE_CREATE_PASSWORD_ROUTE; 17 | } else if (firstTimeFlowType === 'import') { 18 | nextRoute = process.env.ONBOARDING_V2 19 | ? ONBOARDING_IMPORT_WITH_SRP_ROUTE 20 | : INITIALIZE_IMPORT_WITH_SEED_PHRASE_ROUTE; 21 | } else { 22 | nextRoute = DEFAULT_ROUTE; 23 | } 24 | 25 | return nextRoute; 26 | } 27 | 28 | export const getFirstTimeFlowType = (state: any) => { 29 | return state.wallet.firstTimeFlowType; 30 | }; 31 | 32 | export const getOnboardingInitiator = (state: any) => { 33 | const { onboardingTabs } = state.wallet; 34 | 35 | if (!onboardingTabs || Object.keys(onboardingTabs).length !== 1) { 36 | return null; 37 | } 38 | 39 | const location = Object.keys(onboardingTabs)[0]; 40 | const tabId = onboardingTabs[location]; 41 | return { 42 | location, 43 | tabId, 44 | }; 45 | }; 46 | -------------------------------------------------------------------------------- /src/selectors/index.ts: -------------------------------------------------------------------------------- 1 | // export * from './confirm-transaction'; 2 | // export * from './custom-gas'; 3 | export * from './first-time-flow'; 4 | // export * from './metametrics'; 5 | // export * from './permissions'; 6 | // export * from './selectors'; 7 | // export * from './transactions'; 8 | -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/shared/constants/app.ts: -------------------------------------------------------------------------------- 1 | import { RestrictedMethods } from './permissions'; 2 | 3 | /** 4 | * A string representing the type of environment the application is currently running in 5 | * popup - When the user click's the icon in their browser's extension bar; the default view 6 | * notification - When the extension opens due to interaction with a Web3 enabled website 7 | * fullscreen - When the user clicks 'expand view' to open the extension in a new tab 8 | * background - The background process that powers the extension 9 | */ 10 | export type EnvironmentType = 'popup' | 'notification' | 'fullscreen' | 'background'; 11 | export const ENVIRONMENT_TYPE_POPUP = 'popup'; 12 | export const ENVIRONMENT_TYPE_NOTIFICATION = 'notification'; 13 | export const ENVIRONMENT_TYPE_FULLSCREEN = 'fullscreen'; 14 | export const ENVIRONMENT_TYPE_BACKGROUND = 'background'; 15 | 16 | /** 17 | * The distribution this build is intended for. 18 | * 19 | * This should be kept in-sync with the `BuildType` map in `development/build/utils.js`. 20 | */ 21 | export const BuildType = { 22 | beta: 'beta', 23 | flask: 'flask', 24 | main: 'main', 25 | } as const; 26 | 27 | export const PLATFORM_BRAVE = 'Brave'; 28 | export const PLATFORM_CHROME = 'Chrome'; 29 | export const PLATFORM_EDGE = 'Edge'; 30 | export const PLATFORM_FIREFOX = 'Firefox'; 31 | export const PLATFORM_OPERA = 'Opera'; 32 | 33 | export const MESSAGE_TYPE = { 34 | ADD_ETHEREUM_CHAIN: 'wallet_addEthereumChain', 35 | ETH_ACCOUNTS: RestrictedMethods.eth_accounts, 36 | ETH_DECRYPT: 'eth_decrypt', 37 | ETH_GET_ENCRYPTION_PUBLIC_KEY: 'eth_getEncryptionPublicKey', 38 | ETH_REQUEST_ACCOUNTS: 'eth_requestAccounts', 39 | ETH_SIGN: 'eth_sign', 40 | ETH_SIGN_TYPED_DATA: 'eth_signTypedData', 41 | ETH_SIGN_TYPED_DATA_V3: 'eth_signTypedData_v3', 42 | ETH_SIGN_TYPED_DATA_V4: 'eth_signTypedData_v4', 43 | GET_PROVIDER_STATE: 'metamask_getProviderState', 44 | LOG_WEB3_SHIM_USAGE: 'metamask_logWeb3ShimUsage', 45 | PERSONAL_SIGN: 'personal_sign', 46 | SEND_METADATA: 'metamask_sendDomainMetadata', 47 | SWITCH_ETHEREUM_CHAIN: 'wallet_switchEthereumChain', 48 | WALLET_REQUEST_PERMISSIONS: 'wallet_requestPermissions', 49 | WATCH_ASSET: 'wallet_watchAsset', 50 | WATCH_ASSET_LEGACY: 'metamask_watchAsset', 51 | ///: BEGIN:ONLY_INCLUDE_IN(flask) 52 | SNAP_CONFIRM: RestrictedMethods.snap_confirm, 53 | ///: END:ONLY_INCLUDE_IN 54 | } as const; 55 | 56 | /** 57 | * Custom messages to send and be received by the extension 58 | */ 59 | export const EXTENSION_MESSAGES = { 60 | CONNECTION_READY: 'CONNECTION_READY', 61 | READY: 'METAMASK_EXTENSION_READY', 62 | } as const; 63 | 64 | /** 65 | * The different kinds of subjects that Wallet may interact with, including 66 | * third parties and itself (e.g. when the background communicated with the UI). 67 | */ 68 | export const SUBJECT_TYPES = { 69 | EXTENSION: 'extension', 70 | INTERNAL: 'internal', 71 | UNKNOWN: 'unknown', 72 | WEBSITE: 'website', 73 | ///: BEGIN:ONLY_INCLUDE_IN(flask) 74 | SNAP: 'snap', 75 | ///: END:ONLY_INCLUDE_IN 76 | } as const; 77 | 78 | export const POLLING_TOKEN_ENVIRONMENT_TYPES = { 79 | [ENVIRONMENT_TYPE_POPUP]: 'popupGasPollTokens', 80 | [ENVIRONMENT_TYPE_NOTIFICATION]: 'notificationGasPollTokens', 81 | [ENVIRONMENT_TYPE_FULLSCREEN]: 'fullScreenGasPollTokens', 82 | } as const; 83 | 84 | export const ORIGIN_METAMASK = 'metamask'; 85 | 86 | export const METAMASK_BETA_CHROME_ID = 'pbbkamfgmaedccnfkmjcofcecjhfgldn'; 87 | export const METAMASK_PROD_CHROME_ID = 'nkbihfbeogaeaoehlefnkodbefgpgknn'; 88 | export const METAMASK_FLASK_CHROME_ID = 'ljfoeinjpaedjfecbmggjgodbgkmjkjk'; 89 | 90 | export const CHROME_BUILD_IDS = [ 91 | METAMASK_BETA_CHROME_ID, 92 | METAMASK_PROD_CHROME_ID, 93 | METAMASK_FLASK_CHROME_ID, 94 | ] as const; 95 | 96 | const METAMASK_BETA_FIREFOX_ID = 'webextension-beta@metamask.io'; 97 | const METAMASK_PROD_FIREFOX_ID = 'webextension@metamask.io'; 98 | const METAMASK_FLASK_FIREFOX_ID = 'webextension-flask@metamask.io'; 99 | 100 | export const FIREFOX_BUILD_IDS = [ 101 | METAMASK_BETA_FIREFOX_ID, 102 | METAMASK_PROD_FIREFOX_ID, 103 | METAMASK_FLASK_FIREFOX_ID, 104 | ] as const; 105 | 106 | export const UNKNOWN_TICKER_SYMBOL = 'UNKNOWN'; 107 | -------------------------------------------------------------------------------- /src/shared/constants/hardware-wallets.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Accounts can be instantiated from simple, HD or the multiple hardware wallet 3 | * keyring types. Both simple and HD are treated as default but we do special 4 | * case accounts managed by a hardware wallet. 5 | */ 6 | export const HARDWARE_KEYRING_TYPES = { 7 | LEDGER: 'Ledger Hardware', 8 | TREZOR: 'Trezor Hardware', 9 | LATTICE: 'Lattice Hardware', 10 | QR: 'QR Hardware Wallet Device', 11 | }; 12 | 13 | export const DEVICE_NAMES = { 14 | LEDGER: 'ledger', 15 | TREZOR: 'trezor', 16 | QR: 'QR Hardware', 17 | LATTICE: 'lattice', 18 | }; 19 | 20 | export const KEYRING_NAMES = { 21 | LEDGER: 'Ledger', 22 | TREZOR: 'Trezor', 23 | QR: 'QR', 24 | LATTICE: 'Lattice1', 25 | }; 26 | 27 | /** 28 | * Used for setting the users preference for ledger transport type 29 | */ 30 | export const LEDGER_TRANSPORT_TYPES = { 31 | LIVE: 'ledgerLive', 32 | WEBHID: 'webhid', 33 | U2F: 'u2f', 34 | }; 35 | 36 | export const LEDGER_USB_VENDOR_ID = '0x2c97'; 37 | 38 | export const WEBHID_CONNECTED_STATUSES = { 39 | CONNECTED: 'connected', 40 | NOT_CONNECTED: 'notConnected', 41 | UNKNOWN: 'unknown', 42 | }; 43 | 44 | export const TRANSPORT_STATES = { 45 | NONE: 'NONE', 46 | VERIFIED: 'VERIFIED', 47 | DEVICE_OPEN_FAILURE: 'DEVICE_OPEN_FAILURE', 48 | UNKNOWN_FAILURE: 'UNKNOWN_FAILURE', 49 | }; 50 | 51 | export const AFFILIATE_LINKS = { 52 | LEDGER: 'https://shop.ledger.com/?r=17c4991a03fa', 53 | GRIDPLUS: 'https://gridplus.io/?afmc=7p', 54 | TREZOR: 55 | 'https://shop.trezor.io/product/trezor-one-black?offer_id=35&aff_id=11009', 56 | KEYSTONE: 57 | 'https://shop.keyst.one/?rfsn=6088257.656b3e9&utm_source=refersion&utm_medium=affiliate&utm_campaign=6088257.656b3e9', 58 | AIRGAP: 'https://airgap.it/', 59 | COOLWALLET: 'https://www.coolwallet.io/', 60 | DCENT: 'https://dcentwallet.com/', 61 | }; 62 | 63 | export const AFFILIATE_TUTORIAL_LINKS = { 64 | LEDGER: 65 | 'https://support.ledger.com/hc/en-us/articles/4404366864657-Set-up-and-use-MetaMask-to-access-your-Ledger-Ethereum-ETH-account?docs=true', 66 | GRIDPLUS: 'https://docs.gridplus.io/setup/metamask', 67 | TREZOR: 'https://wiki.trezor.io/Apps:MetaMask', 68 | KEYSTONE: 69 | 'https://support.keyst.one/3rd-party-wallets/eth-and-web3-wallets-keystone/bind-metamask-with-keystone', 70 | AIRGAP: 'https://support.airgap.it/guides/metamask/', 71 | COOLWALLET: 'https://www.coolwallet.io/metamask-step-by-step-guides/', 72 | DCENT: 73 | 'https://medium.com/dcentwallet/dcent-wallet-now-supports-qr-based-protocol-to-link-with-metamask-57555f02603f', 74 | }; 75 | -------------------------------------------------------------------------------- /src/shared/constants/keyrings.ts: -------------------------------------------------------------------------------- 1 | import { HARDWARE_KEYRING_TYPES } from './hardware-wallets'; 2 | 3 | export const KEYRING_TYPES = { 4 | HD_KEY_TREE: 'HD Key Tree', 5 | IMPORTED: 'Simple Key Pair', 6 | ...HARDWARE_KEYRING_TYPES, 7 | }; 8 | -------------------------------------------------------------------------------- /src/shared/constants/permissions.ts: -------------------------------------------------------------------------------- 1 | export const CaveatTypes = Object.freeze({ 2 | restrictReturnedAccounts: 'restrictReturnedAccounts' as const, 3 | }); 4 | 5 | export const RestrictedMethods = Object.freeze({ 6 | eth_accounts: 'eth_accounts', 7 | ///: BEGIN:ONLY_INCLUDE_IN(flask) 8 | snap_confirm: 'snap_confirm', 9 | snap_notify: 'snap_notify', 10 | snap_manageState: 'snap_manageState', 11 | snap_getBip32PublicKey: 'snap_getBip32PublicKey', 12 | snap_getBip32Entropy: 'snap_getBip32Entropy', 13 | snap_getBip44Entropy: 'snap_getBip44Entropy', 14 | snap_getEntropy: 'snap_getEntropy', 15 | 'wallet_snap_*': 'wallet_snap_*', 16 | ///: END:ONLY_INCLUDE_IN 17 | } as const); 18 | 19 | ///: BEGIN:ONLY_INCLUDE_IN(flask) 20 | export const PermissionNamespaces = Object.freeze({ 21 | wallet_snap_: 'wallet_snap_*', 22 | } as const); 23 | 24 | export const EndowmentPermissions = Object.freeze({ 25 | 'endowment:network-access': 'endowment:network-access', 26 | 'endowment:long-running': 'endowment:long-running', 27 | 'endowment:transaction-insight': 'endowment:transaction-insight', 28 | 'endowment:cronjob': 'endowment:cronjob', 29 | 'endowment:ethereum-provider': 'endowment:ethereum-provider', 30 | } as const); 31 | 32 | // Methods / permissions in external packages that we are temporarily excluding. 33 | export const ExcludedSnapPermissions = new Set(['snap_dialog']); 34 | export const ExcludedSnapEndowments = new Set(['endowment:keyring']); 35 | ///: END:ONLY_INCLUDE_IN 36 | -------------------------------------------------------------------------------- /src/shared/modules/hexstring-utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | isHexString, 3 | isValidAddress, 4 | isValidChecksumAddress, 5 | addHexPrefix, 6 | toChecksumAddress, 7 | zeroAddress, 8 | isHexPrefixed, 9 | } from 'ethereumjs-util'; 10 | 11 | export const BURN_ADDRESS = zeroAddress(); 12 | 13 | export function isBurnAddress(address: string) { 14 | return address === BURN_ADDRESS; 15 | } 16 | 17 | /** 18 | * Validates that the input is a hex address. This utility method is a thin 19 | * wrapper around ethereumjs-util.isValidAddress, with the exception that it 20 | * does not throw an error when provided values that are not hex strings. In 21 | * addition, and by default, this method will return true for hex strings that 22 | * meet the length requirement of a hex address, but are not prefixed with `0x` 23 | * Finally, if the mixedCaseUseChecksum flag is true and a mixed case string is 24 | * provided this method will validate it has the proper checksum formatting. 25 | * 26 | * @param {string} possibleAddress - Input parameter to check against 27 | * @param {object} [options] - options bag 28 | * @param {boolean} [options.allowNonPrefixed] - If true will first ensure '0x' 29 | * is prepended to the string 30 | * @param {boolean} [options.mixedCaseUseChecksum] - If true will treat mixed 31 | * case addresses as checksum addresses and validate that proper checksum 32 | * format is used 33 | * @returns {boolean} whether or not the input is a valid hex address 34 | */ 35 | export function isValidHexAddress( 36 | possibleAddress: any, 37 | { allowNonPrefixed = true, mixedCaseUseChecksum = false } = {}, 38 | ) { 39 | const addressToCheck = allowNonPrefixed ? addHexPrefix(possibleAddress) : possibleAddress; 40 | if (!isHexString(addressToCheck)) { 41 | return false; 42 | } 43 | 44 | if (mixedCaseUseChecksum) { 45 | const prefixRemoved = addressToCheck.slice(2); 46 | const lower = prefixRemoved.toLowerCase(); 47 | const upper = prefixRemoved.toUpperCase(); 48 | const allOneCase = prefixRemoved === lower || prefixRemoved === upper; 49 | if (!allOneCase) { 50 | return isValidChecksumAddress(addressToCheck); 51 | } 52 | } 53 | 54 | return isValidAddress(addressToCheck); 55 | } 56 | 57 | export function toChecksumHexAddress(address: string) { 58 | if (!address) { 59 | // our internal checksumAddress function that this method replaces would 60 | // return an empty string for nullish input. If any direct usages of 61 | // ethereumjs-util.toChecksumAddress were called with nullish input it 62 | // would have resulted in an error on version 5.1. 63 | return ''; 64 | } 65 | const hexPrefixed = addHexPrefix(address); 66 | if (!isHexString(hexPrefixed)) { 67 | // Version 5.1 of ethereumjs-utils would have returned '0xY' for input 'y' 68 | // but we shouldn't waste effort trying to change case on a clearly invalid 69 | // string. Instead just return the hex prefixed original string which most 70 | // closely mimics the original behavior. 71 | return hexPrefixed; 72 | } 73 | return toChecksumAddress(hexPrefixed); 74 | } 75 | 76 | export function stripHexPrefix(str: string) { 77 | if (typeof str !== 'string') { 78 | return str; 79 | } 80 | return isHexPrefixed(str) ? str.slice(2) : str; 81 | } 82 | -------------------------------------------------------------------------------- /src/shared/modules/mv3.utils.ts: -------------------------------------------------------------------------------- 1 | import browser from 'webextension-polyfill'; 2 | 3 | export const isManifestV3: boolean = browser.runtime.getManifest().manifest_version === 3; 4 | -------------------------------------------------------------------------------- /src/shared/modules/random-id.ts: -------------------------------------------------------------------------------- 1 | const MAX = Number.MAX_SAFE_INTEGER; 2 | 3 | let idCounter = Math.round(Math.random() * MAX); 4 | export default function createRandomId() { 5 | idCounter %= MAX; 6 | // eslint-disable-next-line no-plusplus 7 | return idCounter++; 8 | } 9 | -------------------------------------------------------------------------------- /src/store/action-queue/queue.integration.test.js: -------------------------------------------------------------------------------- 1 | import sinon from 'sinon'; 2 | import PortStream from 'extension-port-stream'; 3 | import { setupMultiplex } from '../../../app/scripts/lib/stream-utils'; 4 | import metaRPCClientFactory from '../../../app/scripts/lib/metaRPCClientFactory'; 5 | 6 | import { 7 | dropQueue, 8 | submitRequestToBackground, 9 | _setBackgroundConnection, 10 | } from '.'; 11 | 12 | jest.mock('../../../shared/modules/mv3.utils', () => { 13 | return { 14 | isManifestV3: () => true, 15 | }; 16 | }); 17 | 18 | const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); 19 | 20 | describe('queue integration test', () => { 21 | afterEach(() => { 22 | dropQueue(true); 23 | }); 24 | it('schedules a retry if background method failed because of a disconnect', async () => { 25 | let disconnectListener; 26 | const extensionPort = { 27 | onMessage: { 28 | addListener: sinon.stub(), 29 | }, 30 | onDisconnect: { 31 | addListener(cb) { 32 | disconnectListener = cb; 33 | }, 34 | }, 35 | postMessage: sinon.stub().callsFake(() => { 36 | disconnectListener(); 37 | }), 38 | }; 39 | 40 | const connectionStream = new PortStream(extensionPort); 41 | const mx = setupMultiplex(connectionStream); 42 | const multiplexStream1 = mx.createStream('controller'); 43 | const background = metaRPCClientFactory(multiplexStream1); 44 | 45 | _setBackgroundConnection(background); 46 | 47 | // disconnect will happen on the attempt to send the message 48 | const finished = submitRequestToBackground('backgroundFunction').catch( 49 | (error) => { 50 | // disconnect error should not get propagated, we retry. 51 | // eslint-disable-next-line jest/no-conditional-expect 52 | expect(error).not.toBeInstanceOf(background.DisconnectError); 53 | // eslint-disable-next-line jest/no-conditional-expect 54 | expect(error.message).toContain('cancelled'); 55 | }, 56 | ); 57 | // We want to make sure we disconnect in the middle of processing, so we have to wait for the control flow to reach postMessage 58 | // undetermined number of asynchronous jumps withing the stream implementation leaves no other option 59 | await wait(3); 60 | // we drop the queue because we're expecting the action to have been returned to the queue and this is the simplest way to check that 61 | dropQueue(); 62 | 63 | expect(extensionPort.postMessage.calledOnce).toStrictEqual(true); 64 | await finished; 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /src/store/actions.ts: -------------------------------------------------------------------------------- 1 | import { generateActionId, callBackgroundMethod, submitRequestToBackground } from './action-queue'; 2 | import * as actionConstants from './actionConstants'; 3 | 4 | export function displayWarning(text: string) { 5 | return { 6 | type: actionConstants.DISPLAY_WARNING, 7 | value: text, 8 | }; 9 | } 10 | 11 | export function showLoadingIndication(message?: string) { 12 | return { 13 | type: actionConstants.SHOW_LOADING, 14 | value: message, 15 | }; 16 | } 17 | 18 | export function hideLoadingIndication() { 19 | return { 20 | type: actionConstants.HIDE_LOADING, 21 | }; 22 | } 23 | 24 | export async function verifySeedPhrase() { 25 | const encodedSeedPhrase = await submitRequestToBackground('verifySeedPhrase'); 26 | return Buffer.from(encodedSeedPhrase).toString('utf8'); 27 | } 28 | 29 | export function createNewVault(password: string) { 30 | console.log('password:', password); 31 | return new Promise((resolve, reject) => { 32 | console.log('here1'); 33 | callBackgroundMethod('createNewVaultAndKeychain', [password], (error: any) => { 34 | console.log('here'); 35 | if (error) { 36 | console.log(error); 37 | reject(error); 38 | return; 39 | } 40 | console.log('success'); 41 | resolve(true); 42 | }); 43 | }); 44 | } 45 | 46 | export async function createNewVaultAndGetSeedPhrase(password: string) { 47 | // return async (dispatch: any) => { 48 | try { 49 | console.log('calling createNewVault'); 50 | const result = await createNewVault(password); 51 | console.log(result); 52 | // await result() 53 | console.log('calling verifySeedPhrase'); 54 | const seedPhrase = await verifySeedPhrase(); 55 | console.log(result); 56 | console.log(seedPhrase); 57 | return seedPhrase; 58 | } catch (error: any) { 59 | console.log(error); 60 | throw new Error(error.message); 61 | } finally { 62 | } 63 | // }; 64 | } 65 | 66 | export function setSeedPhraseBackedUp(seedPhraseBackupState: any) { 67 | return (dispatch: any) => { 68 | return new Promise((resolve, reject) => { 69 | callBackgroundMethod('setSeedPhraseBackedUp', [seedPhraseBackupState], (err: any) => { 70 | if (err) { 71 | // dispatch(displayWarning(err.message)); 72 | reject(err); 73 | return; 74 | } 75 | // forceUpdateMetamaskState(dispatch).then(resolve).catch(reject); 76 | }); 77 | }); 78 | }; 79 | } 80 | 81 | export function setFirstTimeFlowType(type: any) { 82 | return (dispatch: any) => { 83 | callBackgroundMethod('setFirstTimeFlowType', [type], (err: any) => { 84 | if (err) { 85 | dispatch(displayWarning(err.message)); 86 | } 87 | }); 88 | dispatch({ 89 | type: actionConstants.SET_FIRST_TIME_FLOW_TYPE, 90 | value: type, 91 | }); 92 | }; 93 | } 94 | 95 | export function setCompletedOnboarding() { 96 | return async (dispatch: any) => { 97 | dispatch(showLoadingIndication()); 98 | 99 | try { 100 | await submitRequestToBackground('completeOnboarding'); 101 | dispatch(completeOnboarding()); 102 | } catch (err: any) { 103 | dispatch(displayWarning(err.message)); 104 | throw err; 105 | } finally { 106 | dispatch(hideLoadingIndication()); 107 | } 108 | }; 109 | } 110 | 111 | export function completeOnboarding() { 112 | return { 113 | type: actionConstants.COMPLETE_ONBOARDING, 114 | }; 115 | } 116 | -------------------------------------------------------------------------------- /src/store/reducers/index.ts: -------------------------------------------------------------------------------- 1 | import walletReducer from './walletSlice'; 2 | 3 | const rootReducer = { 4 | reducer: { 5 | wallet: walletReducer, 6 | }, 7 | }; 8 | 9 | export default rootReducer; 10 | -------------------------------------------------------------------------------- /src/store/reducers/walletSlice.ts: -------------------------------------------------------------------------------- 1 | import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'; 2 | import { createNewVaultAndGetSeedPhrase } from '../actions'; 3 | 4 | export const createNewAccountThunk = createAsyncThunk( 5 | 'wallet/createNewVaultAndGetSeedPhrase', 6 | async (password: string, thunkAPI) => { 7 | const seed = await createNewVaultAndGetSeedPhrase(password); 8 | console.log('seed', seed); 9 | return seed; 10 | }, 11 | ); 12 | 13 | const walletSlice = createSlice({ 14 | name: 'wallet', 15 | initialState: { 16 | firstTimeFlowType: null, 17 | seed: '', 18 | completedOnboarding: false, 19 | }, 20 | reducers: { 21 | // createNewAccount(state: any, action) { 22 | // const { payload } = action; 23 | // state.seed = seed; 24 | // }, 25 | // todoToggled(state: any[], action) { 26 | // const todo = state.find(todo => todo.id === action.payload) 27 | // todo.completed = !todo.completed 28 | // } 29 | }, 30 | extraReducers: (builder) => { 31 | // Add reducers for additional action types here, and handle loading state as needed 32 | builder.addCase(createNewAccountThunk.fulfilled, (state: any, action) => { 33 | console.log('action:', action); 34 | // Add user to the state array 35 | state.seed = action.payload; 36 | }); 37 | }, 38 | }); 39 | 40 | export default walletSlice.reducer; 41 | -------------------------------------------------------------------------------- /src/store/store.ts: -------------------------------------------------------------------------------- 1 | import { configureStore } from '@reduxjs/toolkit'; 2 | import rootReducer from './reducers'; 3 | 4 | const store = configureStore(rootReducer); 5 | 6 | export default store; 7 | 8 | // Infer the `RootState` and `AppDispatch` types from the store itself 9 | export type RootState = ReturnType; 10 | // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} 11 | export type AppDispatch = typeof store.dispatch; 12 | -------------------------------------------------------------------------------- /src/styles/css/design-system/breakpoints.scss: -------------------------------------------------------------------------------- 1 | @use "sass:map"; 2 | 3 | /* 4 | Responsive breakpoints 5 | */ 6 | 7 | // Screen sizes 8 | $screen-sizes-map: ( 9 | 'sm': 576px, 10 | 'md': 768px, 11 | 'lg': 1280px, 12 | ); 13 | 14 | // Max width screen size 15 | $screen-sm-max: calc(#{map.get($screen-sizes-map, "sm")} - 1px); 16 | $screen-md-max: calc(#{map.get($screen-sizes-map, "md")} - 1px); 17 | $screen-lg-max: calc(#{map.get($screen-sizes-map, "lg")} - 1px); 18 | 19 | // Min width screen size 20 | $screen-sm-min: map.get($screen-sizes-map, "sm"); 21 | $screen-md-min: map.get($screen-sizes-map, "md"); 22 | $screen-lg-min: map.get($screen-sizes-map, "lg"); 23 | 24 | // Max width media query mixins 25 | @mixin screen-sm-max { 26 | @media screen and (max-width: $screen-sm-max) { 27 | @content; 28 | }; 29 | } 30 | 31 | @mixin screen-md-max { 32 | @media screen and (max-width: $screen-md-max) { 33 | @content; 34 | }; 35 | } 36 | 37 | @mixin screen-lg-max { 38 | @media screen and (max-width: $screen-lg-max) { 39 | @content; 40 | }; 41 | } 42 | 43 | // Min width media query mixins 44 | @mixin screen-sm-min { 45 | @media screen and (min-width: $screen-sm-min) { 46 | @content; 47 | }; 48 | } 49 | 50 | @mixin screen-md-min { 51 | @media screen and (min-width: $screen-md-min) { 52 | @content; 53 | }; 54 | } 55 | 56 | @mixin screen-lg-min { 57 | @media screen and (min-width: $screen-lg-min) { 58 | @content; 59 | }; 60 | } 61 | 62 | /* 63 | DEPRECATED 64 | */ 65 | $break-small: 575px; 66 | $break-midpoint: 780px; 67 | $break-large: 576px; 68 | 69 | $screen-sizes-map: ( 70 | 'sm': 576px, 71 | 'md': 768px, 72 | 'lg': 1280px, 73 | ); 74 | -------------------------------------------------------------------------------- /src/styles/css/design-system/index.scss: -------------------------------------------------------------------------------- 1 | // @forward 'attributes'; 2 | @forward 'breakpoints'; 3 | // @forward 'colors'; 4 | // @forward 'deprecated-colors'; 5 | @forward 'typography'; 6 | // @forward 'z-index'; 7 | -------------------------------------------------------------------------------- /src/styles/css/index.scss: -------------------------------------------------------------------------------- 1 | /* 2 | MetaMask design system imports 3 | The variables declared here should take precedence. 4 | They are included first because they will be used to replace bad variable names in itcss 5 | prior to it being fully removed from the system. 6 | */ 7 | // @import './reset.scss'; 8 | @import './design-system/index'; 9 | // @import './utilities/colors.scss'; 10 | // @import './base-styles.scss'; 11 | // @import '../components/component-library/component-library-components.scss'; 12 | // @import '../components/app/app-components'; 13 | // @import '../components/ui/ui-components'; 14 | @import '../../pages/pages'; 15 | // @import './errors.scss'; 16 | // @import './loading.scss'; 17 | 18 | /* 19 | ITCSS 20 | 21 | http://www.creativebloq.com/web-design/manage-large-css-projects-itcss-101517528 22 | https://www.xfive.co/blog/itcss-scalable-maintainable-css-architecture/ 23 | 24 | DEPRECATED: This CSS architecture is deprecated. The following imports will be removed 25 | overtime. 26 | */ 27 | // @import './itcss/settings/index'; 28 | // @import './itcss/tools/index'; 29 | // @import './itcss/components/index'; 30 | 31 | /* 32 | Third Party Library Styles 33 | */ 34 | // @import '../../node_modules/react-tippy/dist/tippy'; 35 | // @import '../../node_modules/@metamask/design-tokens/src/css/design-tokens'; 36 | -------------------------------------------------------------------------------- /src/styles/theme/defaultTheme.ts: -------------------------------------------------------------------------------- 1 | // import { IDefaultTheme } from 'styled-components'; 2 | import { Theme, ThemeOptions, createTheme } from '@mui/material/styles'; 3 | import { green } from '@mui/material/colors'; 4 | 5 | const defaultTheme: Theme = createTheme({ 6 | palette: { 7 | primary: { 8 | main: '#0da3a0', 9 | light: '#0da3a0', 10 | dark: '#0abab5aa', 11 | }, 12 | secondary: { 13 | main: '#282b31', 14 | dark: '#282b31aa', 15 | }, 16 | info: { 17 | main: '#FFFFFF', 18 | dark: '#000000', 19 | }, 20 | background: { 21 | default: '#202328', 22 | paper: '#191c20', 23 | }, 24 | text: { 25 | primary: '#FFFFFF', 26 | secondary: '#AAAAAA', 27 | }, 28 | grey: { 29 | '800': '#333333', 30 | '900': '#191c20', 31 | }, 32 | }, 33 | }); 34 | 35 | export default defaultTheme; 36 | -------------------------------------------------------------------------------- /src/styles/theme/styled.d.ts: -------------------------------------------------------------------------------- 1 | import 'styled-components'; 2 | 3 | declare module 'styled-components' { 4 | export interface IDefaultTheme { 5 | title: string; 6 | dark: string; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/export-utils.ts: -------------------------------------------------------------------------------- 1 | import { getRandomFileName } from './helper'; 2 | 3 | export function exportAsFile(filename: string, data: any, type = 'text/csv') { 4 | // eslint-disable-next-line no-param-reassign 5 | filename = filename || getRandomFileName(); 6 | // source: https://stackoverflow.com/a/33542499 by Ludovic Feltz 7 | const blob = new window.Blob([data], { type }); 8 | if ('msSaveOrOpenBlob' in window.navigator && 'msSaveBlob' in window.navigator && false) { 9 | // window.navigator.msSaveBlob(blob, filename); 10 | } else { 11 | const elem = window.document.createElement('a'); 12 | elem.target = '_blank'; 13 | elem.href = window.URL.createObjectURL(blob); 14 | elem.download = filename; 15 | document.body.appendChild(elem); 16 | elem.click(); 17 | document.body.removeChild(elem); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/helper.tsx: -------------------------------------------------------------------------------- 1 | import * as texts from 'src/locale/en/messages.json'; 2 | 3 | export const array2object = ( 4 | value: Record[] | undefined, 5 | ): Record => 6 | value && value.length 7 | ? value.reduce((a: any, b: any) => ({ 8 | ...a, 9 | ...b, 10 | })) 11 | : {}; 12 | 13 | export const precision = (value: number) => { 14 | return Math.min(Math.floor(value), 3) + 2; 15 | }; 16 | 17 | export const removePrefix = (value: string) => { 18 | return value.startsWith('0x') ? value.replace('0x', '') : value; 19 | }; 20 | 21 | export const defaultNetId = (token: any): string => { 22 | let netId = 1; 23 | if (!Boolean(token?.address)) return netId.toString(); 24 | const netIds = Object.keys(token?.address); 25 | if (!Boolean(netIds?.length)) return netId.toString(); 26 | netId = parseInt(netIds[0]); 27 | return netId.toString(); 28 | }; 29 | 30 | export const findBy = (data: any[], key: string, value: string) => { 31 | if (!Boolean(data)) return null; 32 | return data?.find((el: any) => el[key ?? 'id'] === value) ?? null; 33 | }; 34 | 35 | export const findTokenByNetIdAndAddress = (tokenData: any[], net_id: string, address: string) => { 36 | if (!Boolean(tokenData)) return null; 37 | return tokenData?.find((el: any) => el.address[net_id] === address) ?? null; 38 | }; 39 | 40 | export function clearClipboard() { 41 | window.navigator.clipboard.writeText(''); 42 | } 43 | 44 | export const t = (key: string) => { 45 | return texts[key as keyof typeof texts]?.message ?? `No such message: ${key}`; 46 | }; 47 | 48 | export function getRandomFileName() { 49 | let fileName = ''; 50 | const charBank = [...'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789']; 51 | const fileNameLength = Math.floor(Math.random() * 7 + 6); 52 | 53 | for (let i = 0; i < fileNameLength; i++) { 54 | fileName += charBank[Math.floor(Math.random() * charBank.length)]; 55 | } 56 | 57 | return fileName; 58 | } 59 | -------------------------------------------------------------------------------- /src/utils/i18n-helper.ts: -------------------------------------------------------------------------------- 1 | // cross-browser connection to extension i18n API 2 | import React from 'react'; 3 | import log from 'loglevel'; 4 | import * as Sentry from '@sentry/browser'; 5 | 6 | const warned: any = {}; 7 | const missingMessageErrors: any = {}; 8 | const missingSubstitutionErrors: any = {}; 9 | 10 | /** 11 | * Returns a localized message for the given key 12 | * 13 | * @param {string} localeCode - The code for the current locale 14 | * @param {object} localeMessages - The map of messages for the current locale 15 | * @param {string} key - The message key 16 | * @param {string[]} substitutions - A list of message substitution replacements 17 | * @returns {null|string} The localized message 18 | */ 19 | export const getMessage = (localeCode: any, localeMessages: any, key: any, substitutions: any) => { 20 | if (!localeMessages) { 21 | return null; 22 | } 23 | if (!localeMessages[key]) { 24 | if (localeCode === 'en') { 25 | if (!missingMessageErrors[key]) { 26 | missingMessageErrors[key] = new Error( 27 | `Unable to find value of key "${key}" for locale "${localeCode}"`, 28 | ); 29 | Sentry.captureException(missingMessageErrors[key]); 30 | log.error(missingMessageErrors[key]); 31 | if (process.env.IN_TEST) { 32 | throw missingMessageErrors[key]; 33 | } 34 | } 35 | } else if (!warned[localeCode] || !warned[localeCode][key]) { 36 | if (!warned[localeCode]) { 37 | warned[localeCode] = {}; 38 | } 39 | warned[localeCode][key] = true; 40 | log.warn(`Translator - Unable to find value of key "${key}" for locale "${localeCode}"`); 41 | } 42 | return null; 43 | } 44 | const entry = localeMessages[key]; 45 | let phrase = entry.message; 46 | 47 | const hasSubstitutions = Boolean(substitutions && substitutions.length); 48 | const hasReactSubstitutions = 49 | hasSubstitutions && 50 | substitutions.some( 51 | (element: any) => 52 | element !== null && (typeof element === 'function' || typeof element === 'object'), 53 | ); 54 | 55 | // perform substitutions 56 | if (hasSubstitutions) { 57 | const parts = phrase.split(/(\$\d)/gu); 58 | 59 | const substitutedParts = parts.map((part: any) => { 60 | const subMatch = part.match(/\$(\d)/u); 61 | if (!subMatch) { 62 | return part; 63 | } 64 | const substituteIndex = Number(subMatch[1]) - 1; 65 | if ( 66 | (substitutions[substituteIndex] === null || substitutions[substituteIndex] === undefined) && 67 | !missingSubstitutionErrors[localeCode]?.[key] 68 | ) { 69 | if (!missingSubstitutionErrors[localeCode]) { 70 | missingSubstitutionErrors[localeCode] = {}; 71 | } 72 | missingSubstitutionErrors[localeCode][key] = true; 73 | const error = new Error( 74 | `Insufficient number of substitutions for key "${key}" with locale "${localeCode}"`, 75 | ); 76 | log.error(error); 77 | Sentry.captureException(error); 78 | } 79 | return substitutions[substituteIndex]; 80 | }); 81 | 82 | phrase = hasReactSubstitutions ? substitutedParts : substitutedParts.join(''); 83 | } 84 | 85 | return phrase; 86 | }; 87 | -------------------------------------------------------------------------------- /src/utils/web3.ts: -------------------------------------------------------------------------------- 1 | import Web3 from 'web3'; 2 | import { ETH } from '~/constants/address'; 3 | import ABI from '../constants/abi/ERC20.abi.json'; 4 | 5 | export const getDecimal = async ( 6 | rpc: `https://${string}` | undefined, 7 | address: string, 8 | ): Promise => { 9 | if (address === ETH || rpc === undefined) return '18'; 10 | const web3 = new Web3(rpc); 11 | //@ts-ignore 12 | const tokenContract = new web3.eth.Contract(ABI, address); 13 | const decimal = await tokenContract.methods.decimals().call({}); 14 | return decimal; 15 | }; 16 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": true, 7 | "checkJs": false, 8 | "skipLibCheck": true, 9 | "esModuleInterop": true, 10 | // "noUnusedLocals": true, 11 | // "noUnusedParameters": true, 12 | "noFallthroughCasesInSwitch": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "module": "ESNext", 17 | "moduleResolution": "Node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "emitDecoratorMetadata": true, 21 | "experimentalDecorators": true, 22 | "noEmit": true, 23 | "jsx": "preserve", 24 | "incremental": true, 25 | "baseUrl": ".", 26 | "paths": { 27 | "~/*": ["src/*"] 28 | } 29 | }, 30 | "include": [ 31 | "vite.config.ts", 32 | "**/*.ts", 33 | "**/*.tsx", 34 | "./node_modules/tronweb-typings/index.d.ts", 35 | "./node_modules/@kws1207/types-eth-keyring-controller/index.d.ts", 36 | "litecore-lib-ltc.d.ts", 37 | "src/pages/first-time-flow/seed-phrase/confirm-seed-phrase/index.js" 38 | ] 39 | // "exclude": ["node_modules"] 40 | } 41 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/vite.config.ts --------------------------------------------------------------------------------