├── .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 |
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 |
--------------------------------------------------------------------------------
/src/assets/coingroup/moonpay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codemaster727/GameWalletExtension/967b3b0f3bc41bfdcb26635ba2abecff2313df8c/src/assets/coingroup/moonpay.png
--------------------------------------------------------------------------------
/src/assets/coingroup/optimism.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
--------------------------------------------------------------------------------
/src/assets/coingroup/usdc.svg:
--------------------------------------------------------------------------------
1 |
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 |
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 |
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 |
11 | ) : null;
12 |
13 | export default Icon;
14 |
15 | export const DownIcon = (icon: any, width?: number) =>
16 | icon ? (
17 |
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 |
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 |
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 |
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 |
70 |
91 |
{'importAccountSeedPhrase'}
92 |
{'secretPhrase'}
93 |
99 |
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
--------------------------------------------------------------------------------