├── .github
└── pull_request_template.md
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .travis.yml.disabled
├── Dockerfile
├── LICENSE
├── README.md
├── config-overrides.js
├── package.json
├── public
├── config.json
├── favicon.png
├── index.html
├── manifest.json
└── robots.txt
├── scripts
├── ci_build.sh
└── ci_deploy.sh
├── src
├── colors.ts
├── components
│ ├── access-keystore
│ │ ├── decrypt.worker.ts
│ │ └── index.tsx
│ ├── access-private-key
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ ├── access-tabs
│ │ ├── index.tsx
│ │ └── style.css
│ ├── account-info
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ ├── button
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ ├── copy-to-clipboard
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ ├── disclaimer
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ ├── faucet-pending
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ └── index.tsx
│ ├── faucet-request
│ │ └── index.tsx
│ ├── footer
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ ├── index.tsx
│ │ └── style.css
│ ├── generate-form
│ │ ├── encrypt.worker.ts
│ │ └── index.tsx
│ ├── header
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ ├── index.tsx
│ │ └── style.css
│ ├── layout
│ │ ├── index.tsx
│ │ └── style.css
│ ├── send-form
│ │ └── index.tsx
│ ├── sidebar
│ │ ├── index.tsx
│ │ └── style.css
│ ├── spinner-with-check-mark
│ │ ├── __snapshots__
│ │ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ ├── index.tsx
│ │ └── style.css
│ └── spinner
│ │ ├── __snapshots__
│ │ └── index.test.tsx.snap
│ │ ├── index.test.tsx
│ │ └── index.tsx
├── constants
│ └── index.ts
├── containers
│ ├── faucet
│ │ └── index.tsx
│ ├── generate
│ │ └── index.tsx
│ ├── home
│ │ └── index.tsx
│ └── send
│ │ └── index.tsx
├── contexts
│ └── zil-context
│ │ └── index.tsx
├── index.css
├── index.tsx
├── react-app-env.d.ts
├── reportWebVitals.ts
├── routes.tsx
├── setupTests.ts
├── use-async-fn.test.tsx
├── use-async-fn.ts
├── utils.spec.ts
└── utils.ts
├── tsconfig.json
└── yarn.lock
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Description
2 |
3 |
4 |
5 | ## Motivation and Context
6 |
7 |
8 |
9 |
10 | ## How has this been tested?
11 |
12 |
13 |
14 | ## Screenshots (if appropriate):
15 |
16 | ## Types of changes
17 |
18 |
19 |
20 | - [ ] Bug fix (non-breaking change which fixes an issue)
21 | - [ ] New feature (non-breaking change which adds functionality)
22 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
23 |
24 | ## Checklist:
25 |
26 |
27 |
28 | - [ ] Add tests to cover changes as needed.
29 | - [ ] Update documentation as needed.
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Build folder and files #
2 | ##########################
3 | build/
4 |
5 | # Development folders and files #
6 | #################################
7 | .tmp/
8 | dist/
9 | node_modules/
10 | *.compiled.*
11 | coverage
12 | public
13 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "typescript",
3 | "printWidth": 100,
4 | "singleQuote": true,
5 | "arrowParens": "always",
6 | "overrides": [
7 | {
8 | "files": "*.css",
9 | "options": {
10 | "parser": "css"
11 | }
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/.travis.yml.disabled:
--------------------------------------------------------------------------------
1 |
2 | language: generic
3 | sudo: required
4 | services:
5 | - docker
6 |
7 | matrix:
8 | include:
9 | - os: linux
10 | dist: xenial
11 |
12 | addons:
13 | apt:
14 | update: true
15 | package:
16 | - build-essential
17 | - git
18 | - curl
19 | - zip
20 | - unzip
21 |
22 | before_cache:
23 | - docker save -o docker_images/images.tar $(docker images -aq)
24 |
25 | cache:
26 | directories:
27 | - docker_images
28 |
29 | script: ./scripts/ci_build.sh
30 | skip_cleanup: true
31 |
32 | before_deploy:
33 | - pip install --user awscli
34 | - export PATH=$PATH:$HOME/.local/bin
35 | - docker load -i docker_images/images.tar || true
36 |
37 | deploy:
38 | provider: script
39 | skip_cleanup: true
40 | script:
41 | - ./scripts/ci_deploy.sh
42 | on:
43 | branch: master
44 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:16 as build-stage
2 |
3 | WORKDIR /app
4 | COPY . ./
5 | RUN yarn install
6 | RUN yarn ci
7 | RUN yarn build
8 |
9 | FROM nginx:stable-alpine as production-stage
10 | COPY --from=build-stage /app/build /usr/share/nginx/html
11 | EXPOSE 80
12 | ENTRYPOINT ["nginx", "-g", "daemon off;"]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MOVED: [Dev Wallet](https://dev-wallet.zilliqa.com)
2 |
3 | This repository has been moved to the [Zilliqa Developer](https://github.com/Zilliqa/zilliqa-developer) mono repo. This repository is only kept for historical reasons.
4 |
5 | ## Installation and Usage
6 |
7 | ### `yarn`
8 |
9 | Installs dependencies.
10 |
11 | ### `yarn start`
12 |
13 | Runs the app in development mode.
14 | Open `http://localhost:3000` to view it in the browser.
15 |
16 | ### `yarn test`
17 |
18 | Runs the test watcher in an interactive mode.
19 | We use [Jest](https://jestjs.io/) for testing.
20 |
21 | ## License
22 |
23 | This project is open source software licensed as [GPL-3.0](https://github.com/zilliqa/dev-wallet/blob/master/LICENSE).
24 |
25 |
--------------------------------------------------------------------------------
/config-overrides.js:
--------------------------------------------------------------------------------
1 | const WorkerPlugin = require("worker-plugin");
2 |
3 | module.exports = function override(config, env) {
4 | config.plugins.push(new WorkerPlugin());
5 | return config;
6 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "dev-wallet",
3 | "private": true,
4 | "dependencies": {
5 | "@testing-library/jest-dom": "^5.11.4",
6 | "@testing-library/react": "^11.1.0",
7 | "@testing-library/user-event": "^12.1.10",
8 | "@types/jest": "^26.0.15",
9 | "@types/node": "^12.0.0",
10 | "@types/react": "^17.0.0",
11 | "@types/react-dom": "^17.0.0",
12 | "@types/reactstrap": "^8.0.1",
13 | "@zilliqa-js/zilliqa": "3.3.4",
14 | "bootstrap": "^4.3.1",
15 | "husky": "^1.3.1",
16 | "prettier": "^2.3.2",
17 | "rc-steps": "^3.3.1",
18 | "react": "^17.0.2",
19 | "react-app-rewired": "^2.1.8",
20 | "react-dom": "^17.0.2",
21 | "react-google-recaptcha": "^1.0.5",
22 | "react-hooks-worker": "^1.0.0",
23 | "react-icons": "^3.3.0",
24 | "react-jazzicon": "^0.1.3",
25 | "react-router-dom": "^5.2.0",
26 | "react-scripts": "4.0.3",
27 | "reactstrap": "^8.0.0",
28 | "styled-components": "^4.3.2",
29 | "ts-jest": "^24.0.2",
30 | "typescript": "^4.1.2",
31 | "uuid": "^8.3.2",
32 | "web-vitals": "^1.0.1",
33 | "whatwg-fetch": "^3.0.0",
34 | "worker-plugin": "^5.0.1"
35 | },
36 | "husky": {
37 | "hooks": {
38 | "pre-commit": "yarn ci"
39 | }
40 | },
41 | "scripts": {
42 | "start": "react-app-rewired start",
43 | "build": "react-app-rewired build",
44 | "test": "react-app-rewired test",
45 | "eject": "react-scripts eject",
46 | "test:ci": "CI=true yarn test --coverage --updateSnapshot --forceExit --detectOpenHandles",
47 | "prettier": "prettier \"**/*.+(ts|tsx|css)\"",
48 | "format": "npm run prettier -- --write",
49 | "ci": "npm run prettier -- --list-different && yarn test:ci && tsc --noEmit"
50 | },
51 | "eslintConfig": {
52 | "extends": [
53 | "react-app",
54 | "react-app/jest"
55 | ]
56 | },
57 | "browserslist": {
58 | "production": [
59 | ">0.2%",
60 | "not dead",
61 | "not op_mini all"
62 | ],
63 | "development": [
64 | "last 1 chrome version",
65 | "last 1 firefox version",
66 | "last 1 safari version"
67 | ]
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/public/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "testnet": {
3 | "name": "testnet",
4 | "nodeUrl": "https://dev-api.zilliqa.com",
5 | "chainId": 333,
6 | "msgVersion": 1,
7 | "faucetUrl": "https://stg-testnet-faucet.zilliqa.com/api/v1/faucet",
8 | "explorerUrl": "https://devex.zilliqa.com"
9 | },
10 | "isolated_server": {
11 | "name": "isolated_server",
12 | "nodeUrl": "https://zilliqa-isolated-server.zilliqa.com",
13 | "chainId": 222,
14 | "msgVersion": 1,
15 | "faucetUrl": "https://stg-zilliqa-isolated-faucet.zilliqa.com/api/v1/faucet",
16 | "explorerUrl": "https://devex.zilliqa.com"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Zilliqa/dev-wallet/a921cb9fa77bf9cd1c4cecba8e1818fcda8fb67a/public/favicon.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
16 |
17 |
26 | Dev Wallet
27 |
28 |
29 |
34 |
42 |
43 |
44 | You need to enable JavaScript to run this app.
45 |
46 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Dev Wallet",
3 | "name": "Dev Wallet",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "theme_color": "#000000",
7 | "background_color": "#ffffff"
8 | }
9 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | user-agent: *
2 |
--------------------------------------------------------------------------------
/scripts/ci_build.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo $(pwd)
4 |
5 | docker --version
6 | aws --version
7 |
8 | echo $TRAVIS_COMMIT
9 | commit=$(git rev-parse --short=7 $TRAVIS_COMMIT)
10 |
11 | docker build -t "tempimagebuild:$commit" .
12 |
--------------------------------------------------------------------------------
/scripts/ci_deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo $(pwd)
4 |
5 | docker --version
6 | aws --version
7 |
8 | echo $TRAVIS_COMMIT
9 | commit=$(git rev-parse --short=7 $TRAVIS_COMMIT)
10 |
11 | accountID=$(aws sts get-caller-identity --output text --query 'Account')
12 | regionID=us-west-2
13 | application=dev-wallet
14 | registryURL="zilliqa/$application"
15 |
16 | #eval "$(aws ecr get-login --no-include-email --region $regionID)"
17 | echo "$DOCKER_API_TOKEN" | docker login -u "$DOCKER_USERNAME" --password-stdin
18 |
19 | rm -rf "$application"-artifact
20 | mkdir -p "$application"-artifact/build/
21 |
22 | docker create --name extractbuild "tempimagebuild:$commit"
23 | docker cp extractbuild:/usr/share/nginx/html/. $(pwd)/"$application"-artifact/build/
24 | docker rm extractbuild
25 | docker push "$registryURL"
26 |
27 | cd "$application"-artifact
28 | cd build
29 | echo $commit > "$application"-artifact-commit.txt
30 | zip -r "$application"-artifact.zip .
31 | aws s3 sync . s3://"$application"-static-artifact --exclude='*' --include=''"$application"'-artifact.zip'
32 |
33 | cd ..
34 | echo $(date) > date_created.txt
35 | aws s3 sync . s3://"$application"-static-artifact --exclude='*' --include='date_created.txt'
36 |
--------------------------------------------------------------------------------
/src/colors.ts:
--------------------------------------------------------------------------------
1 | export const colors = {
2 | gray500: '#C5C5D3',
3 | gray600: '#A0A1B2',
4 | teal500: '#12a378',
5 | teal600: '#077d5a',
6 | white: '#FFFFFF',
7 | black: '#24292E',
8 | };
9 |
--------------------------------------------------------------------------------
/src/components/access-keystore/decrypt.worker.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { decryptPrivateKey } from '@zilliqa-js/crypto';
18 |
19 | const decrypt = async (event) => {
20 | try {
21 | const { passphrase, keystoreV3 } = event.data;
22 | const privateKey = await decryptPrivateKey(passphrase, keystoreV3);
23 | // @ts-ignore
24 | // eslint-disable-next-line
25 | self.postMessage({ privateKey });
26 | } catch (error) {
27 | console.log(error);
28 | // @ts-ignore
29 | // eslint-disable-next-line
30 | self.postMessage({ privateKey: undefined });
31 | }
32 | };
33 |
34 | // Worker.ts
35 | // eslint-disable-next-line
36 | const ctx: Worker = self as any;
37 |
38 | // Respond to message from parent thread
39 | ctx.addEventListener('message', (event) => decrypt(event).catch(console.log));
40 |
--------------------------------------------------------------------------------
/src/components/access-keystore/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React, { useState, useEffect } from 'react';
18 | import { Label, Input, FormGroup, Form, FormFeedback } from 'reactstrap';
19 | import Spinner from '../spinner';
20 | import Button from '../button';
21 | import { requestStatus } from '../../constants';
22 |
23 | import { getInputValidationState } from '../../utils';
24 | import Disclaimer from '../disclaimer';
25 |
26 | const formatFilename = (str: string) => {
27 | if (str.length > 35) {
28 | return str.substr(0, 20) + '...' + str.substr(str.length - 20, str.length);
29 | }
30 | return str;
31 | };
32 |
33 | interface IProps {
34 | accessWallet: (privateKey: string) => void;
35 | }
36 |
37 | interface IState {
38 | worker: any;
39 | decryptStatus?: string;
40 | isAccessing: boolean;
41 | passphrase: string;
42 | passphraseValid: boolean;
43 | passphraseInvalid: boolean;
44 | filename: string;
45 | keystoreV3?: any;
46 | isDisclaimerChecked: boolean;
47 | }
48 |
49 | const initialState: IState = {
50 | worker: undefined,
51 | isDisclaimerChecked: false,
52 | decryptStatus: undefined,
53 | passphrase: '',
54 | passphraseValid: false,
55 | passphraseInvalid: false,
56 | filename: '',
57 | keystoreV3: undefined,
58 | isAccessing: false,
59 | };
60 |
61 | const AccessKeystore: React.FunctionComponent = (props) => {
62 | const [worker, setWorker] = useState(initialState.worker);
63 | const [isDisclaimerChecked, setIsDisclaimerChecked] = useState(initialState.isDisclaimerChecked);
64 | const [decryptStatus, setDecryptStatus] = useState(initialState.decryptStatus);
65 |
66 | const [passphrase, setPassphrase] = useState(initialState.passphrase);
67 | const [passphraseValid, setPassphraseValid] = useState(initialState.passphraseValid);
68 | const [passphraseInvalid, setPassphraseInvalid] = useState(initialState.passphraseInvalid);
69 | const [filename, setFilename] = useState(initialState.filename);
70 | const [keystoreV3, setKeystoreV3] = useState(initialState.keystoreV3);
71 |
72 | useEffect(() => {
73 | if (worker === undefined) {
74 | const myWorker = new Worker('./decrypt.worker', { type: 'module' });
75 |
76 | myWorker.onmessage = (event) => {
77 | const { data } = event;
78 | if (data.privateKey === undefined) {
79 | return setDecryptStatus(requestStatus.FAILED);
80 | }
81 |
82 | setDecryptStatus(requestStatus.SUCCEEDED);
83 |
84 | props.accessWallet(data.privateKey);
85 | };
86 | setWorker(myWorker);
87 | }
88 | });
89 |
90 | const handleCheck = () => {
91 | setIsDisclaimerChecked(!isDisclaimerChecked);
92 | };
93 |
94 | const importkeystoreV3 = (e): void => {
95 | e.preventDefault();
96 | try {
97 | const files = e.target.files;
98 | const reader = new FileReader();
99 | reader.onload = () => {
100 | const myFilename = formatFilename(files[0].name);
101 | setFilename(myFilename);
102 | setKeystoreV3(reader.result);
103 | };
104 | reader.readAsText(files[0]);
105 | } catch (error) {
106 | console.log(error);
107 | }
108 | };
109 |
110 | const changePassphrase = (e: React.ChangeEvent): void => {
111 | e.preventDefault();
112 | const value = e.target.value;
113 | const key = 'passphrase';
114 | const validationResult: any = getInputValidationState(key, value, /^.{8,}$/);
115 | setPassphraseValid(validationResult.passphraseValid);
116 | setPassphraseInvalid(validationResult.passphraseInvalid);
117 | setPassphrase(value);
118 | };
119 |
120 | const onSubmit = (e) => {
121 | e.preventDefault();
122 | setDecryptStatus(requestStatus.PENDING);
123 | const keystoreV3Json = JSON.parse(keystoreV3);
124 | worker.postMessage({ passphrase, keystoreV3: keystoreV3Json });
125 | };
126 |
127 | const messageForDecryptFailure = `Decryption failed. Please check your keystore file and passphrase.`;
128 |
129 | const isDecrypting = decryptStatus === requestStatus.PENDING;
130 |
131 | const isSubmitButtonDisabled =
132 | !passphraseValid || keystoreV3 === undefined || isDecrypting || !isDisclaimerChecked;
133 |
134 | const submitButtonText = isDecrypting ? 'Accessing' : 'Access';
135 |
136 | const description = 'You can access your wallet with your keystore file and passphrase.';
137 |
138 | return (
139 |
214 | );
215 | };
216 | export default AccessKeystore;
217 |
--------------------------------------------------------------------------------
/src/components/access-private-key/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot when loading 1`] = `
4 |
87 | `;
88 |
--------------------------------------------------------------------------------
/src/components/access-private-key/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import { render } from '@testing-library/react';
19 | import AccessPrivateKey from '.';
20 |
21 | test('matches the snapshot when loading', () => {
22 | const accessWallet = jest.fn();
23 | const { container } = render( );
24 | expect(container.firstChild).toMatchSnapshot();
25 | });
26 |
--------------------------------------------------------------------------------
/src/components/access-private-key/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React, { useState } from 'react';
18 | import { Label, Input, FormGroup, Form, FormFeedback } from 'reactstrap';
19 | import Spinner from '../spinner';
20 | import Button from '../button';
21 |
22 | import { getInputValidationState } from '../../utils';
23 | import Disclaimer from '../disclaimer';
24 |
25 | interface IProps {
26 | accessWallet: (privateKey: string) => void;
27 | }
28 |
29 | interface IState {
30 | worker: any;
31 | prevAuthStatus?: string;
32 | isAccessing: boolean;
33 | privateKey: string;
34 | privateKeyValid: boolean;
35 | privateKeyInvalid: boolean;
36 | isDisclaimerChecked: boolean;
37 | }
38 |
39 | const initialState: IState = {
40 | worker: undefined,
41 | isDisclaimerChecked: false,
42 | prevAuthStatus: undefined,
43 | isAccessing: false,
44 | privateKey: '',
45 | privateKeyValid: false,
46 | privateKeyInvalid: false,
47 | };
48 |
49 | const AccessPrivateKey: React.FunctionComponent = (props) => {
50 | const [isDisclaimerChecked, setIsDisclaimerChecked] = useState(initialState.isDisclaimerChecked);
51 | const [isAccessing, setIsAccessing] = useState(initialState.isAccessing);
52 | const [privateKey, setPrivateKey] = useState(initialState.privateKey);
53 | const [privateKeyValid, setPrivateKeyValid] = useState(initialState.privateKeyValid);
54 | const [privateKeyInvalid, setPrivateKeyInvalid] = useState(initialState.privateKeyInvalid);
55 |
56 | const handleCheck = () => {
57 | setIsDisclaimerChecked(!isDisclaimerChecked);
58 | };
59 |
60 | const changePrivateKey = (e: React.ChangeEvent): void => {
61 | e.preventDefault();
62 | const value = e.target.value;
63 | const key = 'privateKey';
64 | const validationResult: any = getInputValidationState(key, value, /^[a-fA-F0-9]{64}$/g);
65 | setPrivateKeyValid(validationResult.privateKeyValid);
66 | setPrivateKeyInvalid(validationResult.privateKeyInvalid);
67 | setPrivateKey(value);
68 | };
69 |
70 | const onSubmit = (e) => {
71 | e.preventDefault();
72 | setIsAccessing(true);
73 | return props.accessWallet(privateKey);
74 | };
75 |
76 | const isSubmitButtonDisabled = !privateKeyValid || isAccessing || !isDisclaimerChecked;
77 | const submitButtonText = isAccessing ? 'Accessing' : 'Access';
78 | const description = 'You can access your wallet with private key.';
79 |
80 | return (
81 |
131 | );
132 | };
133 |
134 | export default AccessPrivateKey;
135 |
--------------------------------------------------------------------------------
/src/components/access-tabs/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { useState } from 'react';
18 | import classnames from 'classnames';
19 | import { TabContent, TabPane, Nav, NavItem, NavLink, Card, Row, Col } from 'reactstrap';
20 |
21 | import './style.css';
22 |
23 | import AccessKeystore from '../access-keystore';
24 | import AccessPrivateKey from '../access-private-key';
25 |
26 | const KEYSTORE_TAB = '0';
27 | const PRIVATE_KEY_TAB = '1';
28 |
29 | const AccessTabs = ({ accessWallet }) => {
30 | const [activeTab, setActiveTab] = useState(KEYSTORE_TAB);
31 | const toggle = (tab) => {
32 | if (activeTab !== tab) {
33 | setActiveTab(tab);
34 | }
35 | };
36 |
37 | return (
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | {'Access Existing Wallet'}
46 |
47 |
48 |
49 |
50 |
51 | toggle(KEYSTORE_TAB)}
56 | >
57 | {'keystore File'}
58 |
59 |
60 |
61 | {
66 | toggle(PRIVATE_KEY_TAB);
67 | }}
68 | >
69 | {'Private Key'}
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | );
88 | };
89 |
90 | export default AccessTabs;
91 |
--------------------------------------------------------------------------------
/src/components/access-tabs/style.css:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of this program.
3 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
4 | *
5 | * This program is free software: you can redistribute it and/or modify it under the
6 | * terms of the GNU General Public License as published by the Free Software
7 | * Foundation, either version 3 of the License, or (at your option) any later
8 | * version.
9 | *
10 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
11 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
12 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License along with
15 | * this program. If not, see .
16 | */
17 |
18 | .access-tabs a.nav-link {
19 | color: var(--gray100);
20 | border-radius: 0;
21 | font-weight: bold;
22 | }
23 | .access-tabs a.nav-link.active {
24 | color: var(--gray100);
25 | background-color: var(--gray700);
26 | }
27 |
28 | .access-tabs li.nav-item {
29 | width: 50%;
30 | text-align: center;
31 | }
32 |
--------------------------------------------------------------------------------
/src/components/account-info/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot when failed 1`] = `
4 |
5 |
8 |
9 |
10 | Account Info
11 |
12 |
13 |
16 |
19 |
23 |
30 |
40 |
50 |
60 |
70 |
71 |
72 |
73 |
76 |
77 | Address
78 |
79 |
82 |
87 | zil18j0lvshp0t6ucrqsjkfunpjdg55my3aqenns9v
88 |
89 |
90 |
91 |
92 | (ByStr20: 0x3C9Ff642E17aF5Cc0C109593C9864d4529B247A0)
93 |
94 |
95 |
96 | Public Key
97 |
98 |
99 |
100 | 0245DC2911EDC02F
101 | ...
102 |
103 |
104 |
105 |
106 | Private Key
107 |
108 |
109 |
110 | 0245DC2911EDC02F
111 | ...
112 |
113 |
114 |
115 |
116 | Balance
117 |
122 |
131 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
143 | Something went wrong: undefined
144 |
145 |
146 |
147 |
148 |
149 | `;
150 |
151 | exports[`matches the snapshot when loaded 1`] = `
152 |
153 |
156 |
157 |
158 | Account Info
159 |
160 |
161 |
164 |
167 |
171 |
178 |
188 |
198 |
208 |
218 |
219 |
220 |
221 |
224 |
225 | Address
226 |
227 |
230 |
235 | zil18j0lvshp0t6ucrqsjkfunpjdg55my3aqenns9v
236 |
237 |
238 |
239 |
240 | (ByStr20: 0x3C9Ff642E17aF5Cc0C109593C9864d4529B247A0)
241 |
242 |
243 |
244 | Public Key
245 |
246 |
247 |
248 | 0245DC2911EDC02F
249 | ...
250 |
251 |
252 |
253 |
254 | Private Key
255 |
256 |
257 |
258 | 0245DC2911EDC02F
259 | ...
260 |
261 |
262 |
263 |
264 | Balance
265 |
271 |
280 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
292 |
293 | Loading...
294 |
295 |
296 |
297 |
298 |
299 |
300 | `;
301 |
302 | exports[`matches the snapshot when loaded 2`] = `
303 |
304 |
307 |
308 |
309 | Account Info
310 |
311 |
312 |
315 |
318 |
322 |
329 |
339 |
349 |
359 |
369 |
370 |
371 |
372 |
375 |
376 | Address
377 |
378 |
381 |
386 | zil18j0lvshp0t6ucrqsjkfunpjdg55my3aqenns9v
387 |
388 |
389 |
390 |
391 | (ByStr20: 0x3C9Ff642E17aF5Cc0C109593C9864d4529B247A0)
392 |
393 |
394 |
395 | Public Key
396 |
397 |
398 |
399 | 0245DC2911EDC02F
400 | ...
401 |
402 |
403 |
404 |
405 | Private Key
406 |
407 |
408 |
409 | 0245DC2911EDC02F
410 | ...
411 |
412 |
413 |
414 |
415 | Balance
416 |
421 |
430 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
442 |
443 | 0.0000001 ZIL
444 |
445 |
446 |
447 |
448 |
449 |
450 | `;
451 |
452 | exports[`matches the snapshot when no data 1`] = `
453 |
454 |
457 |
458 |
459 | Account Info
460 |
461 |
462 |
465 |
468 |
472 |
479 |
489 |
499 |
509 |
519 |
520 |
521 |
522 |
525 |
526 | Address
527 |
528 |
531 |
536 | zil18j0lvshp0t6ucrqsjkfunpjdg55my3aqenns9v
537 |
538 |
539 |
540 |
541 | (ByStr20: 0x3C9Ff642E17aF5Cc0C109593C9864d4529B247A0)
542 |
543 |
544 |
545 | Public Key
546 |
547 |
548 |
549 | 0245DC2911EDC02F
550 | ...
551 |
552 |
553 |
554 |
555 | Private Key
556 |
557 |
558 |
559 | 0245DC2911EDC02F
560 | ...
561 |
562 |
563 |
564 |
565 | Balance
566 |
571 |
580 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
592 | No data
593 |
594 |
595 |
596 |
597 |
598 | `;
599 |
--------------------------------------------------------------------------------
/src/components/account-info/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { render, waitFor } from '@testing-library/react';
18 | import AccountInfo from '.';
19 | import { NETWORK } from '../../contexts/zil-context';
20 |
21 | const publicKey = '0245DC2911EDC02F2774E0A40FBEB0112EA60BF513F9EC50889D59FC94C97EC18F';
22 | const privateKey = '0245DC2911EDC02F2774E0A40FBEB0112EA60BF513F9EC50889D59FC94C97EC18F';
23 | const address = '0x3C9Ff642E17aF5Cc0C109593C9864d4529B247A0';
24 | const curNetwork = {
25 | name: NETWORK.TestNet,
26 | chainId: 333,
27 | msgVersion: 1,
28 | nodeUrl: 'https://dev-api.zilliqa.com',
29 | faucetUrl: 'https://some-api.zilliqa.com/api/v1/faucet',
30 | explorerUrl: 'devex.zilliqa.com',
31 | };
32 | test('matches the snapshot when loaded', async () => {
33 | const getBalance = jest.fn().mockResolvedValue('100000');
34 | const { container, getByTestId } = render(
35 |
42 | );
43 |
44 | expect(container.firstChild).toMatchSnapshot();
45 | });
46 |
47 | test('matches the snapshot when loaded', async () => {
48 | const getBalance = jest.fn().mockResolvedValue('100000');
49 | const { container, getByTestId } = render(
50 |
57 | );
58 |
59 | await waitFor(() => getByTestId('container-data'));
60 | expect(container.firstChild).toMatchSnapshot();
61 | });
62 |
63 | test('matches the snapshot when failed', async () => {
64 | const getBalance = jest.fn().mockRejectedValue('Intended Error');
65 | const { container, getByTestId } = render(
66 |
73 | );
74 | await waitFor(() => getByTestId('container-error'), { timeout: 1000 });
75 | expect(container.firstChild).toMatchSnapshot();
76 | });
77 |
78 | test('matches the snapshot when no data', async () => {
79 | const getBalance = jest.fn().mockResolvedValue(undefined);
80 | const { container, getByTestId } = render(
81 |
88 | );
89 | await waitFor(() => getByTestId('container-no-data'));
90 | expect(container.firstChild).toMatchSnapshot();
91 | });
92 |
--------------------------------------------------------------------------------
/src/components/account-info/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import Jazzicon, { jsNumberForAddress } from 'react-jazzicon';
18 | import Button from '../button';
19 | import CopyToClipboard from '../copy-to-clipboard';
20 | import { MdRefresh } from 'react-icons/md';
21 | import { toBech32Address, fromBech32Address } from '@zilliqa-js/crypto';
22 | import { units, BN } from '@zilliqa-js/util';
23 | import { useAsyncFn } from '../../use-async-fn';
24 |
25 | const AccountInfo = ({ privateKey, publicKey, address, getBalance, curNetwork }) => {
26 | const bech32Address = toBech32Address(address);
27 | const { data, error, isPending, run } = useAsyncFn({ fn: getBalance });
28 | const getAddressExplorerURL = (bechAddress) => {
29 | return `${curNetwork.explorerUrl}/address/${bechAddress}?network=${encodeURIComponent(
30 | curNetwork.nodeUrl
31 | )}`;
32 | };
33 |
34 | return (
35 |
36 |
37 |
38 | {'Account Info'}
39 |
40 |
41 |
42 | {bech32Address ? (
43 |
47 | ) : null}
48 |
49 |
50 |
{'Address'}
51 |
52 | {`${bech32Address}`} {' '}
57 |
58 |
59 | {`(ByStr20: ${address})`}
60 |
61 |
{'Public Key'}
62 |
63 | {publicKey.slice(0, 16)}...
64 |
65 |
{'Private Key'}
66 |
67 | {privateKey.slice(0, 16)}...
68 |
69 |
70 | {'Balance'}
71 | }
75 | onClick={run}
76 | disabled={isPending}
77 | className="mb-1 py-0 px-1"
78 | />
79 |
80 | {isPending ? (
81 |
82 | Loading...
83 |
84 | ) : error ? (
85 |
{`Something went wrong: ${error.message}`}
86 | ) : data ? (
87 |
88 | {`${units.fromQa(new BN(data as string), units.Units.Zil)} ZIL`}
89 |
90 | ) : (
91 |
No data
92 | )}
93 |
94 |
95 |
96 |
97 | );
98 | };
99 |
100 | export default AccountInfo;
101 |
--------------------------------------------------------------------------------
/src/components/button/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot 1`] = `
4 |
11 |
12 | primary small
13 |
14 |
15 | `;
16 |
17 | exports[`matches the snapshot 2`] = `
18 |
24 |
25 | primary small
26 |
27 |
28 | `;
29 |
30 | exports[`matches the snapshot 3`] = `
31 |
38 |
39 | primary medium
40 |
41 |
42 | `;
43 |
44 | exports[`matches the snapshot 4`] = `
45 |
51 |
52 | primary medium
53 |
54 |
55 | `;
56 |
57 | exports[`matches the snapshot 5`] = `
58 |
65 |
66 | primary large
67 |
68 |
69 | `;
70 |
71 | exports[`matches the snapshot 6`] = `
72 |
78 |
79 | primary large
80 |
81 |
82 | `;
83 |
84 | exports[`matches the snapshot 7`] = `
85 |
92 |
93 | secondary small
94 |
95 |
96 | `;
97 |
98 | exports[`matches the snapshot 8`] = `
99 |
105 |
106 | secondary small
107 |
108 |
109 | `;
110 |
111 | exports[`matches the snapshot 9`] = `
112 |
119 |
120 | secondary medium
121 |
122 |
123 | `;
124 |
125 | exports[`matches the snapshot 10`] = `
126 |
132 |
133 | secondary medium
134 |
135 |
136 | `;
137 |
138 | exports[`matches the snapshot 11`] = `
139 |
146 |
147 | secondary large
148 |
149 |
150 | `;
151 |
152 | exports[`matches the snapshot 12`] = `
153 |
159 |
160 | secondary large
161 |
162 |
163 | `;
164 |
165 | exports[`matches the snapshot 13`] = `
166 |
173 |
174 | tertiary small
175 |
176 |
177 | `;
178 |
179 | exports[`matches the snapshot 14`] = `
180 |
186 |
187 | tertiary small
188 |
189 |
190 | `;
191 |
192 | exports[`matches the snapshot 15`] = `
193 |
200 |
201 | tertiary medium
202 |
203 |
204 | `;
205 |
206 | exports[`matches the snapshot 16`] = `
207 |
213 |
214 | tertiary medium
215 |
216 |
217 | `;
218 |
219 | exports[`matches the snapshot 17`] = `
220 |
227 |
228 | tertiary large
229 |
230 |
231 | `;
232 |
233 | exports[`matches the snapshot 18`] = `
234 |
240 |
241 | tertiary large
242 |
243 |
244 | `;
245 |
246 | exports[`matches the snapshot 19`] = `
247 |
253 |
254 | Pre
255 |
256 |
257 | before
258 |
259 |
260 | `;
261 |
262 | exports[`matches the snapshot 20`] = `
263 |
269 |
270 | before
271 |
272 |
273 | Post
274 |
275 |
276 | `;
277 |
--------------------------------------------------------------------------------
/src/components/button/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { render, cleanup } from '@testing-library/react';
18 | import Button from '.';
19 |
20 | // automatically unmount and cleanup DOM after the test is finished.
21 | afterEach(cleanup);
22 |
23 | const levelArray = ['primary', 'secondary', 'tertiary'];
24 | const sizeArray = ['small', 'medium', 'large'];
25 | const disabledArray = [true, false];
26 | const snapshot = (level) => (size) => (disabled) => {
27 | test('matches the snapshot', () => {
28 | const onClick = jest.fn();
29 | const { container } = render(
30 |
38 | );
39 | expect(container.firstChild).toMatchSnapshot();
40 | });
41 | };
42 |
43 | levelArray.forEach((level) =>
44 | sizeArray.forEach((size) => disabledArray.forEach((disabled) => snapshot(level)(size)(disabled)))
45 | );
46 |
47 | test('matches the snapshot', () => {
48 | const onClick = jest.fn();
49 | const { container } = render(
50 | Pre}
53 | level={'primary'}
54 | data-testid={'button-before'}
55 | onClick={onClick}
56 | />
57 | );
58 | expect(container.firstChild).toMatchSnapshot();
59 | });
60 |
61 | test('matches the snapshot', () => {
62 | const onClick = jest.fn();
63 | const { container } = render(
64 | Post}
67 | level={'primary'}
68 | data-testid={'button-before'}
69 | onClick={onClick}
70 | />
71 | );
72 | expect(container.firstChild).toMatchSnapshot();
73 | });
74 |
--------------------------------------------------------------------------------
/src/components/button/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import * as React from 'react';
18 | import styled from 'styled-components';
19 | import { colors } from '../../colors';
20 |
21 | const StyledButton = styled.button`
22 | outline: none;
23 | margin: 0;
24 | font-family: inherit;
25 | overflow: visible;
26 | text-transform: none;
27 | display: inline-block;
28 | font-weight: 400;
29 | text-align: center;
30 | vertical-align: middle;
31 | -webkit-user-select: none;
32 | -moz-user-select: none;
33 | -ms-user-select: none;
34 | user-select: none;
35 | background-color: transparent;
36 | border: 1px solid transparent;
37 | transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out,
38 | border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
39 |
40 | &::-webkit-file-upload-button {
41 | font: inherit;
42 | -webkit-appearance: button;
43 | }
44 |
45 | & [type='button'],
46 | [type='reset'],
47 | [type='submit'] {
48 | -webkit-appearance: button;
49 | }
50 |
51 | &:not(:disabled),
52 | [type='button']:not(:disabled),
53 | [type='reset']:not(:disabled),
54 | [type='submit']:not(:disabled) {
55 | cursor: pointer;
56 | }
57 |
58 | &::-moz-focus-inner,
59 | [type='button']::-moz-focus-inner,
60 | [type='reset']::-moz-focus-inner,
61 | [type='submit']::-moz-focus-inner {
62 | padding: 0;
63 | border-style: none;
64 | }
65 |
66 | &:disabled {
67 | cursor: not-allowed;
68 | }
69 |
70 | ${({ size }) =>
71 | size === 'large'
72 | ? `&{
73 | padding: 0.5rem 1rem;
74 | font-size: 1.25rem;
75 | line-height: 1.5;
76 | border-radius: 0.3rem;
77 | }`
78 | : size === 'small'
79 | ? `&{
80 | padding: 0.25rem 0.5rem;
81 | font-size: 0.875rem;
82 | line-height: 1.5;
83 | border-radius: 0.2rem;
84 | }`
85 | : `&{
86 | padding: 0.375rem 0.75rem;
87 | font-size: 1rem;
88 | line-height: 1.5;
89 | border-radius: 0.25rem;
90 | }`}
91 |
92 | ${({ level }) =>
93 | level === 'primary'
94 | ? `&{
95 | background-color: ${colors.teal600};
96 | color: ${colors.white};
97 | border-color: transparent;
98 | }&:hover,:active,:focus{
99 | background-color: ${colors.teal500};
100 | color: ${colors.white};
101 | border-color: 'transparent';
102 | }&:disabled{
103 | color: ${colors.gray500};
104 | background-color: ${colors.teal600};
105 | }`
106 | : level === 'secondary'
107 | ? `&{
108 | background-color: transparent;
109 | color: ${colors.gray500};
110 | border-color: ${colors.gray500};
111 | }&:hover,:active,:focus{
112 | color: ${colors.white};
113 | border-color: ${colors.white};
114 | }&:disabled{
115 | color: ${colors.gray600};
116 | border-color: ${colors.gray600};
117 | }`
118 | : level === 'tertiary'
119 | ? `&{
120 | background-color: transparent;
121 | color: ${colors.gray500};
122 | border-color: transparent;
123 | }&:hover,:active,:focus{
124 | color: ${colors.white};
125 | }&:disabled{
126 | color: ${colors.gray600};
127 | }`
128 | : ``}
129 | `;
130 |
131 | interface IProps {
132 | readonly level: ButtonLevelType;
133 | readonly size?: SizeType;
134 | readonly text?: string;
135 | readonly onClick?: (e?) => void;
136 | readonly id?: string;
137 | readonly disabled?: boolean;
138 | readonly before?: React.ReactNode;
139 | readonly after?: React.ReactNode;
140 | readonly className?: string;
141 | readonly type?: ButtonType;
142 | readonly style?: object;
143 | readonly testId?: string;
144 | }
145 |
146 | type ButtonType = 'button' | 'submit' | 'reset';
147 | type SizeType = 'large' | 'medium' | 'small';
148 | type ButtonLevelType = 'primary' | 'secondary' | 'tertiary';
149 |
150 | const Button: React.FunctionComponent = ({
151 | text = '',
152 | size = 'medium',
153 | className = '',
154 | type = 'submit',
155 | disabled,
156 | onClick,
157 | before,
158 | after,
159 | style,
160 | level,
161 | testId,
162 | ...rest
163 | }) => (
164 |
175 | {before ? before : null} {text} {after ? after : null}
176 |
177 | );
178 |
179 | export default Button;
180 |
--------------------------------------------------------------------------------
/src/components/copy-to-clipboard/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot 1`] = `
4 |
11 |
14 |
23 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | `;
33 |
--------------------------------------------------------------------------------
/src/components/copy-to-clipboard/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import { render, cleanup } from '@testing-library/react';
19 | import CopyToClipboard from '.';
20 |
21 | // automatically unmount and cleanup DOM after the test is finished.
22 | afterEach(cleanup);
23 |
24 | test('matches the snapshot', () => {
25 | const copy = jest.fn();
26 | const { container } = render( );
27 | expect(container.firstChild).toMatchSnapshot();
28 | });
29 |
--------------------------------------------------------------------------------
/src/components/copy-to-clipboard/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { useState } from 'react';
18 | import Button from '../button';
19 | import { FaCopy, FaCheck, FaTimes } from 'react-icons/fa';
20 |
21 | enum ASYNC_STATUS {
22 | Idle = 'Idle',
23 | Pending = 'Pending',
24 | Fulfilled = 'Fulfilled',
25 | Rejected = 'Rejected',
26 | }
27 |
28 | const CopyToClipboard = ({ data, copyToClipboard }) => {
29 | const [copyStatus, setCopyStatus] = useState(ASYNC_STATUS.Idle);
30 | const handleCopyAddress = async () => {
31 | setCopyStatus(ASYNC_STATUS.Pending);
32 | try {
33 | await copyToClipboard(data);
34 | setCopyStatus(ASYNC_STATUS.Fulfilled);
35 | setTimeout(() => setCopyStatus(ASYNC_STATUS.Idle), 1000);
36 | } catch (error) {
37 | setCopyStatus(ASYNC_STATUS.Rejected);
38 | setTimeout(() => setCopyStatus(ASYNC_STATUS.Idle), 1000);
39 | }
40 | };
41 | return copyToClipboard !== undefined ? (
42 |
51 |
52 |
53 | ) : copyStatus === ASYNC_STATUS.Rejected ? (
54 |
55 |
56 |
57 | ) : (
58 |
59 |
60 |
61 | )
62 | }
63 | disabled={copyStatus !== ASYNC_STATUS.Idle}
64 | onClick={handleCopyAddress}
65 | />
66 | ) : null;
67 | };
68 |
69 | const Copy = (props) => (
70 | window.navigator.clipboard.writeText(text)
77 | : undefined
78 | }
79 | />
80 | );
81 |
82 | export default Copy;
83 |
--------------------------------------------------------------------------------
/src/components/disclaimer/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot 1`] = `
4 |
8 | This is Zilliqa
9 |
10 | Dev Wallet
11 |
12 | . Do not send any interim ERC-20 ZIL tokens or Mainnet tokens to this wallet. You are solely responsible for your account funds.
13 |
14 | `;
15 |
--------------------------------------------------------------------------------
/src/components/disclaimer/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import { render } from '@testing-library/react';
19 |
20 | import Disclaimer from '.';
21 |
22 | test('matches the snapshot', () => {
23 | const { container } = render( );
24 | expect(container.firstChild).toMatchSnapshot();
25 | });
26 |
--------------------------------------------------------------------------------
/src/components/disclaimer/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | const Disclaimer = () => (
18 |
19 | This is Zilliqa Dev Wallet . Do not send any interim ERC-20 ZIL tokens or Mainnet tokens
20 | to this wallet. You are solely responsible for your account funds.
21 |
22 | );
23 |
24 | export default Disclaimer;
25 |
--------------------------------------------------------------------------------
/src/components/faucet-pending/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot 1`] = `
4 |
8 |
11 | Running Faucet
12 |
13 |
14 | Please kindly wait. It might take a while.
15 |
16 |
17 |
18 | `;
19 |
--------------------------------------------------------------------------------
/src/components/faucet-pending/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import { render } from '@testing-library/react';
19 | import FaucetPending from '.';
20 |
21 | test('matches the snapshot', () => {
22 | const { container } = render( );
23 | expect(container.firstChild).toMatchSnapshot();
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/faucet-pending/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | const FaucetPending = () => (
18 |
19 |
20 | {'Running Faucet'}
21 |
22 | {'Please kindly wait. It might take a while.'}
23 |
24 |
25 | );
26 |
27 | export default FaucetPending;
28 |
--------------------------------------------------------------------------------
/src/components/faucet-request/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { useState } from 'react';
18 | import SpinnerWithCheckMark from '../spinner-with-check-mark';
19 | import Button from '../button';
20 | import FaucetPending from '../faucet-pending';
21 |
22 | import { SITE_KEY } from '../../constants';
23 | import ReCAPTCHA from 'react-google-recaptcha';
24 | import { useAsyncFn } from '../../use-async-fn';
25 |
26 | const FaucetRequest = ({ faucet, toAddress, reset }) => {
27 | const { error, isPending, isFulfilled, run } = useAsyncFn({
28 | fn: faucet,
29 | deferred: true,
30 | });
31 | const [token, setToken] = useState();
32 |
33 | return (
34 |
35 | {isPending ? (
36 |
37 |
38 |
39 |
40 | ) : error ? (
41 |
42 |
43 |
44 | {error.message}
45 |
46 | {'Google reCAPTCHA might not work for some country.'}
47 |
48 |
49 |
50 |
51 |
52 | ) : isFulfilled ? (
53 |
54 |
55 |
56 |
57 | {'Transaction In Process'}
58 |
59 |
60 | {'Your transaction is pending blockchain confirmation.'}
61 |
62 | {'Please check after a few minutes.'}
63 |
64 |
65 |
66 |
67 |
68 | ) : (
69 |
70 |
71 | setToken(recaptchaToken)}
74 | badge="inline"
75 | />
76 |
77 |
78 |
run({ token, toAddress })}
81 | level="primary"
82 | disabled={token === undefined}
83 | />
84 |
85 | )}
86 |
87 | );
88 | };
89 |
90 | export default FaucetRequest;
91 |
--------------------------------------------------------------------------------
/src/components/footer/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot 1`] = `
4 |
122 | `;
123 |
--------------------------------------------------------------------------------
/src/components/footer/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import { render } from '@testing-library/react';
19 | import Footer from '.';
20 |
21 | test('matches the snapshot', () => {
22 | const { container } = render();
23 | expect(container.firstChild).toMatchSnapshot();
24 | });
25 |
--------------------------------------------------------------------------------
/src/components/footer/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { FaGithub, FaDiscord, FaTelegramPlane, FaTwitter } from 'react-icons/fa';
18 | import './style.css';
19 |
20 | const Footer = ({ year }) => {
21 | const getCopyright = (y) => `Copyright © ${y} Zilliqa Research Pte. Ltd.`;
22 | return (
23 |
24 |
25 |
71 |
{getCopyright(year)}
72 |
73 |
74 | );
75 | };
76 |
77 | export default Footer;
78 |
--------------------------------------------------------------------------------
/src/components/footer/style.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | .footer {
18 | padding-top: 30px;
19 | }
20 |
21 | .copyright {
22 | font-size: 0.8rem;
23 | line-height: 1.8;
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/generate-form/encrypt.worker.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { schnorr, encryptPrivateKey } from '@zilliqa-js/crypto';
18 |
19 | const encrypt = async (event) => {
20 | try {
21 | const { passphrase } = event.data;
22 | const privateKey = schnorr.generatePrivateKey();
23 | const keystoreJSON = await encryptPrivateKey('pbkdf2', privateKey, passphrase);
24 | // @ts-ignore
25 | // eslint-disable-next-line
26 | self.postMessage({ keystoreJSON, privateKey });
27 | } catch (error) {
28 | console.log(error);
29 | // @ts-ignore
30 | // eslint-disable-next-line
31 | self.postMessage({ keystoreJSON: undefined, privateKey: undefined });
32 | }
33 | };
34 |
35 | // Worker.ts
36 | // eslint-disable-next-line
37 | const ctx: Worker = self as any;
38 |
39 | // Respond to message from parent thread
40 | ctx.addEventListener('message', (event) => encrypt(event).catch(console.log));
41 |
--------------------------------------------------------------------------------
/src/components/generate-form/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React, { useState, useEffect } from 'react';
18 | import { Card, Label, Input, FormGroup, Form, Row, Col, FormFeedback } from 'reactstrap';
19 | import Steps, { Step } from 'rc-steps';
20 | import Button from '../button';
21 | import Spinner from '../spinner';
22 |
23 | import { getInputValidationState, downloadObjectAsJson } from '../../utils';
24 | import { requestStatus } from '../../constants';
25 | import Disclaimer from '../disclaimer';
26 |
27 | const FIRST_STEP = 0;
28 | const SECOND_STEP = 1;
29 | const FINAL_STEP = 2;
30 |
31 | const GenerateForm: React.FunctionComponent = () => {
32 | const [currentStep, setCurrentStep] = useState(FIRST_STEP);
33 | const [privateKey, setPrivateKey] = useState();
34 | const [passphrase, setPassphrase] = useState('');
35 |
36 | return (
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | {currentStep === FIRST_STEP ? (
50 |
55 | ) : currentStep === SECOND_STEP ? (
56 |
62 | ) : currentStep === FINAL_STEP ? (
63 |
64 | ) : null}
65 |
66 |
67 |
68 |
69 |
70 | );
71 | };
72 |
73 | const PassphraseStep = ({ passphrase, setPassphrase, setCurrentStep }) => {
74 | const [passphraseValid, setPassphraseValid] = useState(false);
75 | const [passphraseInvalid, setPassphraseInvalid] = useState(false);
76 | const [isDisclaimerChecked, setIsDisclaimerChecked] = useState(false);
77 | const handleCheck = () => {
78 | setIsDisclaimerChecked(!isDisclaimerChecked);
79 | };
80 |
81 | const changePassphrase = (e: React.ChangeEvent): void => {
82 | e.preventDefault();
83 | const value = e.target.value;
84 | const key = 'passphrase';
85 | const validationResult: any = getInputValidationState(key, value, /^.{8,}$/);
86 | setPassphrase(value);
87 | setPassphraseValid(validationResult.passphraseValid);
88 | setPassphraseInvalid(validationResult.passphraseInvalid);
89 | };
90 |
91 | const isDisabled = !passphraseValid || !isDisclaimerChecked;
92 |
93 | return (
94 |
95 |
96 |
97 | {'Set Passphrase for your Keystore File'}
98 |
99 |
100 | {`Please set the password for the keystore file for your new wallet.`}
101 |
102 |
103 |
148 |
149 | );
150 | };
151 |
152 | const KeystoreStep = ({ setCurrentStep, setPrivateKey, privateKey, passphrase }) => {
153 | const [worker, setWorker] = useState();
154 | const [encryptStatus, setEncryptStatus] = useState(requestStatus.INITIAL);
155 | const [keystoreJSON, setKeystoreJSON] = useState('');
156 |
157 | const downloadKeystore = () => {
158 | if (keystoreJSON === '') {
159 | return;
160 | }
161 | const keystoreObject = JSON.parse(keystoreJSON);
162 | const filename = `zilliqa_keystore_${new Date().toISOString()}`;
163 |
164 | setCurrentStep(FINAL_STEP);
165 | setEncryptStatus(requestStatus.SUCCEEDED);
166 | downloadObjectAsJson(keystoreObject, filename);
167 | };
168 |
169 | useEffect(() => {
170 | if (privateKey && keystoreJSON) {
171 | downloadKeystore();
172 | }
173 | }, [privateKey, keystoreJSON]);
174 |
175 | useEffect(() => {
176 | if (worker === undefined) {
177 | const myWorker = new Worker('./encrypt.worker', { type: 'module' });
178 |
179 | myWorker.onmessage = (event) => {
180 | const { data } = event;
181 | if (data.keystoreJSON === undefined || data.privateKey === undefined) {
182 | return setEncryptStatus(requestStatus.FAILED);
183 | }
184 | setKeystoreJSON(data.keystoreJSON);
185 | setPrivateKey(data.privateKey);
186 | };
187 | setWorker(myWorker as any);
188 | }
189 | });
190 |
191 | const generateKeystore = () => {
192 | setEncryptStatus(requestStatus.PENDING);
193 | //@ts-ignore
194 | worker.postMessage({ passphrase });
195 | };
196 |
197 | const isPending = encryptStatus === requestStatus.PENDING;
198 | const buttonText = isPending ? 'Generating Keystore File' : 'Generate Keystore File';
199 | return (
200 |
201 |
202 |
203 | {'Generate Keystore File'}
204 |
205 |
206 | {`The password for your keystore file for a new wallet has been set. Please click the tab below to generate your keystore to setup your wallet and move on to the last step.`}
207 |
208 |
209 |
210 |
217 |
218 |
219 | ) : null
220 | }
221 | disabled={isPending}
222 | />
223 |
224 |
225 | );
226 | };
227 |
228 | const FinalStep = ({ privateKey }) => {
229 | return (
230 |
231 |
232 |
233 | {'Please Save Your Private Key'}
234 |
235 |
236 | {`Your new wallet has been created.`}
237 |
238 | {`Make sure to copy the private key below and save it.`}
239 |
240 |
241 |
242 | Private Key
243 |
244 |
245 | {privateKey}
246 |
247 |
248 | );
249 | };
250 |
251 | export default GenerateForm;
252 |
--------------------------------------------------------------------------------
/src/components/header/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot 1`] = `
4 |
114 | `;
115 |
116 | exports[`matches the snapshot 2`] = `
117 |
232 | `;
233 |
--------------------------------------------------------------------------------
/src/components/header/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import { render } from '@testing-library/react';
19 | import Header from '.';
20 | import { MemoryRouter } from 'react-router';
21 | import { NETWORK } from '../../contexts/zil-context';
22 |
23 | test('matches the snapshot', () => {
24 | const clearAuth = jest.fn();
25 | const switchNetwork = jest.fn();
26 | const isAuth = false;
27 | const { container } = render(
28 |
29 |
42 |
43 | );
44 | expect(container).toMatchSnapshot();
45 | });
46 |
47 | test('matches the snapshot', () => {
48 | const clearAuth = jest.fn();
49 | const switchNetwork = jest.fn();
50 | const isAuth = true;
51 | const { container } = render(
52 |
53 |
66 |
67 | );
68 | expect(container).toMatchSnapshot();
69 | });
70 |
--------------------------------------------------------------------------------
/src/components/header/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React, { useState } from 'react';
18 | import { Navbar, Nav, NavItem, NavLink, NavbarBrand, Collapse, NavbarToggler } from 'reactstrap';
19 | import './style.css';
20 | import { Link } from 'react-router-dom';
21 | import { paths } from '../../routes';
22 | import { ButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';
23 | import { NETWORK } from '../../contexts/zil-context';
24 |
25 | const Header = ({ curNetwork, isAuth, clearAuth, switchNetwork }) => {
26 | const [isOpen, setOpen] = useState(false);
27 | const [dropdownOpen, setDropdownOpen] = useState(false);
28 | const toggle = () => setDropdownOpen((prevState) => !prevState);
29 |
30 | return (
31 |
32 |
33 | {'Dev Wallet'}
34 |
35 | setOpen(!isOpen)} aria-label="toggler" />
36 |
37 |
38 |
39 |
40 |
41 | {'Home'}
42 |
43 |
44 |
45 |
46 | {'Create New Wallet'}
47 |
48 |
49 |
50 |
51 | {'Send ZIL'}
52 |
53 |
54 |
55 |
56 | {'ZIL Faucet'}
57 |
58 |
59 | {isAuth ? (
60 |
61 | {'Sign Out'}
62 |
63 | ) : null}
64 |
65 |
66 |
67 |
68 | {curNetwork.name
69 | .split('_')
70 | .map((cur) => cur.slice(0, 1).toUpperCase() + cur.slice(1))
71 | .join(' ')}
72 |
73 |
74 |
75 | switchNetwork(NETWORK.TestNet)}>Testnet
76 | switchNetwork(NETWORK.IsolatedServer)}>
77 | Isolated Server
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | );
86 | };
87 |
88 | export default Header;
89 |
--------------------------------------------------------------------------------
/src/components/header/style.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | .network-button {
18 | border-color: var(--success);
19 | background-color: var(--black);
20 | }
21 |
22 | .network {
23 | font-size: 13px;
24 | font-weight: 500;
25 | min-width: 148px;
26 | color: var(--success);
27 | text-align: center;
28 | line-height: 24px;
29 | }
30 |
31 | .network::before {
32 | content: '';
33 | width: 8px;
34 | height: 8px;
35 | display: inline-block;
36 | vertical-align: middle;
37 | background-color: var(--success);
38 | border-radius: 50%;
39 | margin-right: 6px;
40 | }
41 |
42 | @media only screen and (min-width: 575px) {
43 | .sidebar-link {
44 | display: none;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/components/layout/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import Sidebar from '../sidebar';
18 | import Header from '../header';
19 | import Footer from '../footer';
20 | import './style.css';
21 |
22 | const Layout = ({ zilContext, children }) => {
23 | const { isAuth, clearAuth, curNetwork, switchNetwork } = zilContext;
24 | return (
25 |
26 |
32 |
33 |
34 |
35 | {children}
36 |
37 |
38 |
39 |
40 | );
41 | };
42 |
43 | export default Layout;
44 |
--------------------------------------------------------------------------------
/src/components/layout/style.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | .layout {
18 | padding-top: 3.5rem;
19 | }
20 |
21 | @media only screen and (max-width: 575px) {
22 | .content-section {
23 | margin-left: 0 !important;
24 | }
25 | }
26 |
27 | .content-section {
28 | margin-left: 210px;
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/send-form/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React, { useState } from 'react';
18 | import { Card, Label, Input, FormGroup, Form, Row, Col, FormFeedback } from 'reactstrap';
19 | import { BN, units } from '@zilliqa-js/util';
20 | import Button from '../button';
21 | import { getInputValidationState, formatSendAmountInZil, setValIfWholeNum } from '../../utils';
22 | import SpinnerWithCheckMark from '../spinner-with-check-mark';
23 | import Disclaimer from '../disclaimer';
24 |
25 | import { isBech32 } from '@zilliqa-js/util/dist/validation';
26 | import { useAsyncFn } from '../../use-async-fn';
27 |
28 | const SendForm = ({ send, getBalance, getMinGasPrice, curNetwork }) => {
29 | const [hasRun, setHasRun] = useState(false);
30 | const [isDraft, setIsDraft] = useState(true);
31 | const [toAddress, setToAddress] = useState('');
32 | const [toAddressValid, setToAddressValid] = useState(false);
33 | const [toAddressInvalid, setToAddressInvalid] = useState(false);
34 | const [amount, setAmount] = useState('');
35 |
36 | const minGasProps = useAsyncFn({ fn: getMinGasPrice });
37 | const minGasPriceInQa = minGasProps.data as string;
38 | const isUpdatingMinGasPrice = minGasProps.isPending;
39 |
40 | const minGasPriceInZil: string = units.fromQa(new BN(minGasPriceInQa), units.Units.Zil);
41 |
42 | const balanceProps = useAsyncFn({ fn: getBalance });
43 | const balanceInQa = balanceProps.data as string;
44 | const isUpdatingBalance = balanceProps.isPending;
45 |
46 | const balanceInZil: string = units.fromQa(new BN(balanceInQa), units.Units.Zil);
47 |
48 | const mutationProps = useAsyncFn({
49 | fn: send,
50 | deferred: true,
51 | });
52 |
53 | const confirm = () => {
54 | setIsDraft(true);
55 | setHasRun(false);
56 | setToAddress('');
57 | setToAddressValid(false);
58 | setToAddressInvalid(false);
59 | setAmount('');
60 | };
61 |
62 | const changeToAddress = (e: React.ChangeEvent): void => {
63 | e.preventDefault();
64 | const value = e.target.value;
65 | const key = 'toAddress';
66 | const validationResult: any = getInputValidationState(key, value, isBech32(value));
67 | setToAddress(value);
68 | setToAddressValid(validationResult.toAddressValid);
69 | setToAddressInvalid(validationResult.toAddressInvalid);
70 | };
71 |
72 | const formatAmount = async (): Promise => {
73 | await balanceProps.run();
74 | if (amount !== '') {
75 | const amountInZil: string = parseFloat(amount).toFixed(3);
76 | const amountFormattedInZil = formatSendAmountInZil(
77 | amountInZil,
78 | balanceInZil,
79 | minGasPriceInZil
80 | );
81 | setAmount(amountFormattedInZil);
82 | }
83 | };
84 |
85 | const isBalanceInsufficient = new BN(balanceInQa).lte(new BN(minGasPriceInQa));
86 | const isSendButtonDisabled =
87 | toAddressInvalid || toAddress === '' || amount === '' || isBalanceInsufficient;
88 |
89 | return (
90 |
91 |
92 |
93 |
94 |
95 |
96 | {'Send'}
97 |
98 |
99 | {isDraft ? (
100 |
101 |
102 |
167 |
168 |
169 | ) : (
170 |
171 |
172 | {hasRun ? (
173 |
178 | ) : (
179 |
180 |
188 |
189 | )}
190 |
191 |
192 | )}
193 |
194 |
195 |
196 |
197 |
198 | );
199 | };
200 |
201 | const TransactionProcess = ({ confirm, mutationProps, curNetwork }) => {
202 | const { isFulfilled, isPending, error, data } = mutationProps;
203 | const getTxExplorerURL = (txId) => {
204 | return `${curNetwork.explorerUrl}/tx/${txId}?network=${encodeURIComponent(curNetwork.nodeUrl)}`;
205 | };
206 | return (
207 |
208 | {isPending ? (
209 |
210 |
211 |
212 |
213 |
214 | {'Sending Transaction'}
215 |
216 | {'Please kindly wait.'}
217 |
218 |
219 | ) : error ? (
220 |
{error.message}
221 | ) : isFulfilled ? (
222 | <>
223 |
224 |
225 |
226 |
227 | {'Transaction In Process'}
228 |
229 |
230 | {'the transaction is pending blockchain confirmation.'}
231 |
232 | {'Please check after a few minutes.'}
233 |
234 | {data ? (
235 |
236 |
237 | {'View Your Transaction'}
238 |
239 |
240 | ) : null}
241 |
242 |
243 |
244 |
245 | >
246 | ) : null}
247 |
248 | );
249 | };
250 |
251 | const CreateForm = ({ setIsDraft, toAddress, amount, gasPrice, setHasRun, mutationProps }) => {
252 | const { isPending, run, error } = mutationProps;
253 | const [isDisclaimerChecked, setIsDisclaimerChecked] = useState(false);
254 |
255 | const onSubmit = () => {
256 | setHasRun(true);
257 | run({ toAddress, amount });
258 | };
259 |
260 | const isSubmitButtonDisabled = isPending || !isDisclaimerChecked;
261 | const submitButtonText = 'Confirm';
262 | return (
263 |
264 |
265 | Transaction Info:
266 |
267 |
268 |
269 | {'To Address'}
270 |
271 | {toAddress}
272 |
273 |
274 | {'Amount to Send'}
275 |
276 | {amount} ZIL
277 |
278 |
279 | {'Gas Price'}
280 |
281 | {gasPrice} ZIL
282 |
283 |
284 |
308 |
309 | );
310 | };
311 |
312 | export default SendForm;
313 |
--------------------------------------------------------------------------------
/src/components/sidebar/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { withRouter } from 'react-router';
18 | import { NavItem } from 'reactstrap';
19 | import { Link } from 'react-router-dom';
20 | import { paths } from '../../routes';
21 | import './style.css';
22 | import { FaHome, FaPlusSquare, FaTint, FaPaperPlane } from 'react-icons/fa';
23 |
24 | const Sidebar = (props) => {
25 | const { pathname } = props.location;
26 |
27 | const renderLink = (path, name, icon) => (
28 |
32 | {icon}
33 | {name}
34 |
35 | );
36 |
37 | return (
38 |
39 |
40 |
41 |
42 | {renderLink(paths.home, 'Home', )}
43 | {renderLink(paths.generate, 'Create New Wallet', )}
44 | {renderLink(paths.send, 'Access Wallet', )}
45 | {renderLink(paths.faucet, 'ZIL Faucet', )}
46 |
47 |
48 |
49 |
50 | );
51 | };
52 |
53 | export default withRouter(Sidebar);
54 |
--------------------------------------------------------------------------------
/src/components/sidebar/style.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | .sidebar-icon {
18 | position: relative;
19 | top: -0.1rem;
20 | }
21 |
22 | .sidebar-background {
23 | background-position: 50%;
24 | position: fixed;
25 | left: 210px;
26 | width: 210px;
27 | height: 100%;
28 | margin-left: -210px;
29 | z-index: 999;
30 | }
31 |
32 | .sidebar-wrapper {
33 | position: fixed;
34 | left: 210px;
35 | width: 210px;
36 | height: 100%;
37 | margin-left: -210px;
38 | overflow-y: auto;
39 | z-index: 1000;
40 | -webkit-transition: all 0.5s ease;
41 | -moz-transition: all 0.5s ease;
42 | -o-transition: all 0.5s ease;
43 | transition: all 0.5s ease;
44 | }
45 |
46 | .sidebar-nav {
47 | position: absolute;
48 | top: 20px;
49 | width: 210px;
50 | margin: 0;
51 | padding: 0;
52 | list-style: none;
53 | }
54 |
55 | .sidebar-nav li {
56 | line-height: 40px;
57 | }
58 |
59 | .sidebar-nav li a {
60 | display: block;
61 | text-decoration: none;
62 | color: var(--gray500);
63 | font-size: 16px;
64 | }
65 |
66 | .sidebar-nav li a.active,
67 | .sidebar-nav li a:hover {
68 | text-decoration: none;
69 | color: var(--white);
70 | }
71 |
72 | .sidebar-nav li a:focus {
73 | text-decoration: none;
74 | color: var(--white);
75 | }
76 |
77 | @media only screen and (max-width: 575px) {
78 | .sidebar {
79 | display: none;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/components/spinner-with-check-mark/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot when loaded 1`] = `
4 |
11 | `;
12 |
13 | exports[`matches the snapshot when loading 1`] = `
14 |
21 | `;
22 |
--------------------------------------------------------------------------------
/src/components/spinner-with-check-mark/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import { render } from '@testing-library/react';
19 | import Spinner from '.';
20 |
21 | test('matches the snapshot when loading', () => {
22 | const { container } = render( );
23 | expect(container.firstChild).toMatchSnapshot();
24 | });
25 |
26 | test('matches the snapshot when loaded', () => {
27 | const { container } = render( );
28 | expect(container.firstChild).toMatchSnapshot();
29 | });
30 |
--------------------------------------------------------------------------------
/src/components/spinner-with-check-mark/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import './style.css';
19 |
20 | interface IProps {
21 | loading: boolean;
22 | }
23 |
24 | const SpinnerWithCheckMark: React.FunctionComponent = ({ loading }) => {
25 | return (
26 |
29 | );
30 | };
31 |
32 | export default SpinnerWithCheckMark;
33 |
--------------------------------------------------------------------------------
/src/components/spinner-with-check-mark/style.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | .circle-loader {
18 | border: 2px solid rgba(0, 0, 0, 0.1);
19 | border-left-color: var(--success);
20 | animation: loader-spin 1.2s infinite linear;
21 | position: relative;
22 | display: inline-block;
23 | vertical-align: top;
24 | border-radius: 50%;
25 | width: 7em;
26 | height: 7em;
27 | }
28 |
29 | .load-complete {
30 | -webkit-animation: none;
31 | animation: none;
32 | border-color: var(--success);
33 | transition: border 500ms ease-out;
34 | }
35 |
36 | .checkmark {
37 | display: none;
38 | }
39 |
40 | .load-complete > .checkmark {
41 | display: block;
42 | }
43 |
44 | .checkmark.draw::after {
45 | animation-duration: 800ms;
46 | animation-timing-function: ease;
47 | animation-name: checkmark;
48 | transform: scaleX(-1) rotate(135deg);
49 | }
50 |
51 | .checkmark::after {
52 | opacity: 1;
53 | height: 3.5em;
54 | width: 1.75em;
55 | transform-origin: left top;
56 | border-right: 3px solid var(--success);
57 | border-top: 3px solid var(--success);
58 | content: '';
59 | left: 1.75em;
60 | top: 3.5em;
61 | position: absolute;
62 | }
63 |
64 | @keyframes loader-spin {
65 | 0% {
66 | transform: rotate(0deg);
67 | }
68 |
69 | 100% {
70 | transform: rotate(360deg);
71 | }
72 | }
73 |
74 | @keyframes checkmark {
75 | 0% {
76 | height: 0;
77 | width: 0;
78 | opacity: 1;
79 | }
80 |
81 | 20% {
82 | height: 0;
83 | width: 1.75em;
84 | opacity: 1;
85 | }
86 |
87 | 40% {
88 | height: 3.5em;
89 | width: 1.75em;
90 | opacity: 1;
91 | }
92 |
93 | 100% {
94 | height: 3.5em;
95 | width: 1.75em;
96 | opacity: 1;
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/components/spinner/__snapshots__/index.test.tsx.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`matches the snapshot (large) 1`] = `
4 |
8 | `;
9 |
10 | exports[`matches the snapshot (medium) 1`] = `
11 |
15 | `;
16 |
17 | exports[`matches the snapshot (small) 1`] = `
18 |
22 | `;
23 |
--------------------------------------------------------------------------------
/src/components/spinner/index.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import { render, cleanup } from '@testing-library/react';
19 | import Spinner from '.';
20 |
21 | // automatically unmount and cleanup DOM after the test is finished.
22 | afterEach(cleanup);
23 |
24 | test('matches the snapshot (small)', () => {
25 | const { container } = render( );
26 | expect(container.firstChild).toMatchSnapshot();
27 | });
28 | test('matches the snapshot (medium)', () => {
29 | const { container } = render( );
30 | expect(container.firstChild).toMatchSnapshot();
31 | });
32 | test('matches the snapshot (large)', () => {
33 | const { container } = render( );
34 | expect(container.firstChild).toMatchSnapshot();
35 | });
36 |
--------------------------------------------------------------------------------
/src/components/spinner/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import * as React from 'react';
18 | import styled from 'styled-components';
19 | import { colors } from '../../colors';
20 |
21 | type SizeType = 'large' | 'medium' | 'small';
22 |
23 | interface IProps {
24 | readonly size?: SizeType;
25 | }
26 |
27 | const SMALL = 15;
28 | const MEDIUM = 50;
29 | const LARGE = 80;
30 |
31 | const StyledSpinner = styled.div`
32 | border: 2px solid rgba(0, 0, 0, 0.1);
33 | border-left-color: ${colors.gray500};
34 | animation: loader-spin 1s infinite linear;
35 | position: relative;
36 | display: inline-block;
37 | vertical-align: middle;
38 | border-radius: 50%;
39 |
40 | ${({ size }) =>
41 | size === 'large'
42 | ? `&{
43 | width: ${LARGE}px;
44 | height: ${LARGE}px;
45 | }`
46 | : size === 'small'
47 | ? `&{
48 | width: ${SMALL}px;
49 | height: ${SMALL}px;
50 | }`
51 | : `&{
52 | width: ${MEDIUM}px;
53 | height: ${MEDIUM}px;
54 | }`}
55 |
56 | @keyframes loader-spin {
57 | 0% {
58 | transform: rotate(0deg);
59 | }
60 | 100% {
61 | transform: rotate(360deg);
62 | }
63 | }
64 | `;
65 |
66 | const Spinner: React.FunctionComponent = ({ size, ...rest }) => (
67 |
68 | );
69 |
70 | export default Spinner;
71 |
--------------------------------------------------------------------------------
/src/constants/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | export enum requestStatus {
18 | INITIAL = 'INITIAL',
19 | PENDING = 'PENDING',
20 | FAILED = 'FAILED',
21 | SUCCEEDED = 'SUCCEEDED',
22 | }
23 |
24 | export const SITE_KEY: string = '6LcC7DYcAAAAAA10VKIpA0sK44zrqn6rFIbxqYxd';
25 |
--------------------------------------------------------------------------------
/src/containers/faucet/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React, { useState } from 'react';
18 | import { Card, FormGroup, Label, Input, FormFeedback, Row, Col, Form } from 'reactstrap';
19 | import Layout from '../../components/layout';
20 | import FaucetRequest from '../../components/faucet-request';
21 |
22 | import { getInputValidationState } from '../../utils';
23 | import { validation } from '@zilliqa-js/util';
24 | const { isAddress, isBech32 } = validation;
25 |
26 | const FaucetContainer = ({ zilContext }) => {
27 | const { faucet } = zilContext;
28 |
29 | const urlSearchParams = new URLSearchParams(window.location.search);
30 | const params = Object.fromEntries(urlSearchParams.entries());
31 | const initialAddress = params['address'];
32 |
33 | let isInitialAddressValid = false;
34 | if (initialAddress !== undefined) {
35 | isInitialAddressValid = isBech32(initialAddress) || isAddress(initialAddress);
36 | }
37 |
38 | const [toAddress, setToAddress] = useState(initialAddress);
39 | const [toAddressValid, setToAddressValid] = useState(isInitialAddressValid);
40 | const [toAddressInvalid, setToAddressInvalid] = useState(
41 | initialAddress !== undefined && !isInitialAddressValid
42 | );
43 |
44 | const changeToAddress = (e: React.ChangeEvent): void => {
45 | e.preventDefault();
46 | const value = e.target.value;
47 | const key = 'toAddress';
48 |
49 | const validate = (value) => isBech32(value) || isAddress(value);
50 |
51 | const validationResult: any = getInputValidationState(key, value, validate(value));
52 | setToAddress(value);
53 | setToAddressValid(validationResult.toAddressValid);
54 | setToAddressInvalid(validationResult.toAddressInvalid);
55 | };
56 |
57 | const reset = () => {
58 | setToAddress('');
59 | setToAddressValid(false);
60 | setToAddressInvalid(false);
61 | };
62 |
63 | return (
64 |
65 |
66 |
67 |
68 |
69 | {'ZIL Faucet'}
70 |
71 |
72 | {'Please run the faucet to receive a small amount of Zil for testing.'}
73 |
74 |
75 |
76 |
99 |
100 |
101 | {toAddressValid ? (
102 |
103 | ) : null}
104 |
105 |
106 |
107 |
108 | );
109 | };
110 |
111 | export default FaucetContainer;
112 |
--------------------------------------------------------------------------------
/src/containers/generate/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import Layout from '../../components/layout';
18 | import GenerateForm from '../../components/generate-form';
19 |
20 | const CreateContainer = ({ zilContext }) => {
21 | return (
22 |
23 |
24 |
25 | );
26 | };
27 |
28 | export default CreateContainer;
29 |
--------------------------------------------------------------------------------
/src/containers/home/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import Layout from '../../components/layout';
18 | import Row from 'reactstrap/lib/Row';
19 | import Col from 'reactstrap/lib/Col';
20 | import Disclaimer from '../../components/disclaimer';
21 | import styled from 'styled-components';
22 |
23 | const Container = styled.div`
24 | width: 100%;
25 | height: 30rem;
26 | display: flex;
27 | flexdirection: row;
28 | justify-content: center;
29 | align-items: center;
30 | background: #112;
31 | `;
32 |
33 | const Home = ({ zilContext }) => {
34 | const { curNetwork } = zilContext;
35 | return (
36 |
37 |
38 |
45 |
46 | {' '}
47 | Dev Wallet
48 |
49 |
50 |
51 |
52 |
53 | Node URL: {curNetwork.nodeUrl}
54 |
55 |
56 |
57 | Chain ID: {curNetwork.chainId}
58 |
59 | {' | '}
60 |
61 | Msg Ver: {curNetwork.msgVersion}
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | );
75 | };
76 |
77 | export default Home;
78 |
--------------------------------------------------------------------------------
/src/containers/send/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import AccessTabs from '../../components/access-tabs';
18 | import Layout from '../../components/layout';
19 | import SendForm from '../../components/send-form';
20 | import AccountInfo from '../../components/account-info';
21 |
22 | const SendContainer = (props) => {
23 | const { zilContext } = props;
24 | const {
25 | isAuth,
26 | privateKey,
27 | publicKey,
28 | address,
29 | accessWallet,
30 | getBalance,
31 | getMinGasPrice,
32 | send,
33 | curNetwork,
34 | } = zilContext;
35 | return (
36 |
37 | {isAuth ? (
38 | <>
39 |
46 |
52 | >
53 | ) : (
54 | <>
55 |
56 | >
57 | )}
58 |
59 | );
60 | };
61 | export default SendContainer;
62 |
--------------------------------------------------------------------------------
/src/contexts/zil-context/index.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import React from 'react';
18 | import {
19 | getAddressFromPrivateKey,
20 | getPubKeyFromPrivateKey,
21 | fromBech32Address,
22 | } from '@zilliqa-js/crypto';
23 | import { Long, units, BN, validation } from '@zilliqa-js/util';
24 | import { Transaction } from '@zilliqa-js/account';
25 |
26 | import { bytes, Zilliqa } from '@zilliqa-js/zilliqa';
27 | import { HTTPProvider, RPCMethod } from '@zilliqa-js/core';
28 |
29 | export enum NETWORK {
30 | IsolatedServer = 'isolated_server',
31 | TestNet = 'testnet',
32 | }
33 |
34 | const initialState = {
35 | config: undefined as any,
36 |
37 | curNetwork: undefined as any,
38 | zilliqa: undefined as Zilliqa | undefined,
39 | version: undefined as number | undefined,
40 | provider: undefined as HTTPProvider | undefined,
41 |
42 | isAuth: undefined as boolean | undefined,
43 | address: undefined as string | undefined,
44 | publicKey: undefined as string | undefined,
45 | privateKey: undefined as string | undefined,
46 | };
47 | export const ZilContext = React.createContext(initialState);
48 |
49 | export class ZilProvider extends React.Component {
50 | public readonly state = initialState;
51 | async componentDidMount() {
52 | const res = await fetch('config.json');
53 | const config = await res.json();
54 | console.log('config', config);
55 | this.setState({ config }, () => this.initState());
56 | }
57 |
58 | initState = (networkKey?: string) => {
59 | let curNetworkKey = networkKey || NETWORK.TestNet;
60 |
61 | const urlSearchParams = new URLSearchParams(window.location.search);
62 | const params = Object.fromEntries(urlSearchParams.entries());
63 | const networkParam = params['network'];
64 | const isValidNetwork = [NETWORK.IsolatedServer, NETWORK.TestNet].includes(
65 | networkParam as NETWORK
66 | );
67 | if (isValidNetwork) {
68 | curNetworkKey = networkParam;
69 | }
70 |
71 | if (networkParam === undefined || !isValidNetwork) {
72 | const urlSearchParams = new URLSearchParams(window.location.search);
73 | urlSearchParams.set('network', curNetworkKey);
74 | window.history.replaceState(
75 | null,
76 | '',
77 | `${window.location.pathname}?${urlSearchParams.toString()}`
78 | );
79 | }
80 | const { config } = this.state;
81 |
82 | const curNetwork =
83 | curNetworkKey === NETWORK.TestNet ? config[NETWORK.TestNet] : config[NETWORK.IsolatedServer];
84 |
85 | const provider = new HTTPProvider(curNetwork.nodeUrl);
86 | const zilliqa = new Zilliqa(curNetwork.nodeUrl, provider);
87 | const version = bytes.pack(curNetwork.chainId, curNetwork.msgVersion);
88 |
89 | this.setState({
90 | curNetwork: curNetwork,
91 | zilliqa: zilliqa,
92 | version: version,
93 | provider: provider,
94 |
95 | isAuth: undefined as boolean | undefined,
96 | address: undefined as string | undefined,
97 | publicKey: undefined as string | undefined,
98 | privateKey: undefined as string | undefined,
99 | });
100 | };
101 |
102 | public accessWallet = (privateKey: string) => {
103 | try {
104 | const address = getAddressFromPrivateKey(privateKey);
105 | const publicKey = getPubKeyFromPrivateKey(privateKey);
106 | const zilliqa = this.state.zilliqa as Zilliqa;
107 | zilliqa.wallet.addByPrivateKey(privateKey);
108 |
109 | this.setState({
110 | isAuth: true,
111 | privateKey,
112 | publicKey,
113 | address,
114 | zilliqa,
115 | });
116 | } catch (error) {
117 | this.setState({ isAuth: false });
118 | }
119 | };
120 |
121 | private getParams = async (toAddr, amountInZil) => {
122 | const zilliqa = this.state.zilliqa as Zilliqa;
123 | const version = this.state.version as number;
124 | const response = await zilliqa.blockchain.getMinimumGasPrice();
125 | const gasPrice: string = response.result || '';
126 |
127 | const amountInQa = units.toQa(amountInZil, units.Units.Zil);
128 | return {
129 | toAddr,
130 | version: version,
131 | amount: amountInQa,
132 | gasPrice: new BN(gasPrice.toString()),
133 | gasLimit: Long.fromNumber(50),
134 | };
135 | };
136 |
137 | public send = async ({ args }): Promise => {
138 | const { amount, toAddress } = args;
139 | const zilliqa = this.state.zilliqa as Zilliqa;
140 | const provider = this.state.provider as HTTPProvider;
141 | const tx = new Transaction(await this.getParams(toAddress, amount), provider);
142 | const signedTx = await zilliqa.wallet.sign(tx);
143 | const { txParams } = signedTx;
144 | // Send a transaction to the network
145 | const res = await provider.send(RPCMethod.CreateTransaction, txParams);
146 | if (res.error !== undefined) throw new Error(res.error.message);
147 | return res.result ? res.result.TranID : undefined;
148 | };
149 |
150 | public getBalance = async (): Promise => {
151 | const zilliqa = this.state.zilliqa as Zilliqa;
152 | const address = this.state.address as string;
153 |
154 | if (typeof address !== 'string') {
155 | return '0';
156 | }
157 | const res = await zilliqa.blockchain.getBalance(address);
158 | if (res.error !== undefined) return '0';
159 | return res.result ? res.result.balance : '0';
160 | };
161 |
162 | public getMinGasPrice = async (): Promise => {
163 | const zilliqa = this.state.zilliqa as Zilliqa;
164 | const res = await zilliqa.blockchain.getMinimumGasPrice();
165 | if (res.error !== undefined) throw new Error(res.error.message);
166 | return res.result ? res.result : '0';
167 | };
168 |
169 | public faucet = async ({ args, signal }): Promise => {
170 | const { token, toAddress } = args;
171 |
172 | let address = toAddress;
173 | // address is either ByStr20 or bech32
174 | //
175 | // ByStr20: 20 byte hexadecimal string
176 | // e.g. 0x573EC96638C8bB1c386394602E1460634F02aDdA
177 | //
178 | // bech32: A bech32 with a human-readable prefix of zil
179 | // e.g. zil12ulvje3ceza3cwrrj3szu9rqvd8s9tw69c978p
180 |
181 | if (validation.isBech32(toAddress)) {
182 | address = fromBech32Address(toAddress);
183 | }
184 |
185 | const body = JSON.stringify({
186 | address,
187 | token,
188 | });
189 | const { curNetwork } = this.state;
190 | const res = await fetch(curNetwork.faucetUrl, {
191 | signal,
192 | method: 'POST',
193 | headers: {
194 | 'Content-Type': 'application/json',
195 | },
196 | body,
197 | });
198 | if (!res.ok) {
199 | throw new Error('Failed to run faucet, you may have reached maximum request limit.');
200 | }
201 | const data = await res.json();
202 | return data ? data.txId : undefined;
203 | };
204 |
205 | public clearAuth = () => {
206 | const { curNetwork } = this.state;
207 | this.initState(curNetwork.name);
208 | };
209 |
210 | public switchNetwork = (key) => {
211 | const urlSearchParams = new URLSearchParams(window.location.search);
212 | urlSearchParams.set('network', key);
213 | window.history.replaceState(
214 | null,
215 | '',
216 | `${window.location.pathname}?${urlSearchParams.toString()}`
217 | );
218 | this.initState(key);
219 | };
220 |
221 | public render() {
222 | return (
223 |
237 | {this.props.children}
238 |
239 | );
240 | }
241 | }
242 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | :root {
18 | --white: #fff;
19 | --black: #001;
20 | --gray100: #f9f9fd;
21 | --gray200: #e9e9f4;
22 | --gray300: #c5c5d3;
23 | --gray400: #a0a1b2;
24 | --gray500: #7c7d8c;
25 | --gray600: #525360;
26 | --gray700: #343546;
27 | --gray800: #112;
28 | --success: #12a378;
29 | --danger: coral;
30 | }
31 |
32 | .neon {
33 | color: #fff;
34 | /* text-shadow: 0 0 2px #fff, 0 0 16px var(--success), 0 0 32px var(--success),
35 | 0 0 64px var(--success); */
36 |
37 | animation: GlowEffect 5s linear infinite;
38 | }
39 |
40 | @keyframes GlowEffect {
41 | 0% {
42 | text-shadow: 0 0 4px var(--white), 0 0 16px var(--success), 0 0 64px var(--success);
43 | }
44 |
45 | 50% {
46 | text-shadow: 0 0 4px var(--white), 0 0 16px var(--success), 0 0 64px var(--success),
47 | 0 0 72px royalblue;
48 | }
49 |
50 | 100% {
51 | text-shadow: 0 0 4px var(--white), 0 0 16px var(--success), 0 0 64px var(--success);
52 | }
53 | }
54 |
55 | .font-monospace {
56 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
57 | }
58 |
59 | code {
60 | color: var(--gray300);
61 | }
62 |
63 | body {
64 | color: var(--gray100);
65 | background-color: var(--black);
66 | margin: 0;
67 | padding: 0;
68 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
69 | 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
70 | -webkit-font-smoothing: antialiased;
71 | -moz-osx-font-smoothing: grayscale;
72 | overflow-x: scroll;
73 | }
74 |
75 | .bg-dark {
76 | background-color: var(--black) !important;
77 | }
78 |
79 | .text-primary {
80 | color: var(--white) !important;
81 | }
82 |
83 | .text-secondary {
84 | color: var(--gray300) !important;
85 | }
86 |
87 | .valid-feedback,
88 | .text-success {
89 | color: var(--success) !important;
90 | }
91 |
92 | .form-control.is-valid {
93 | border-color: var(--success) !important;
94 | background-image: none;
95 | padding-right: 0;
96 | }
97 |
98 | .invalid-feedback,
99 | .text-danger {
100 | color: var(--danger) !important;
101 | }
102 |
103 | .card {
104 | background-color: var(--gray800);
105 | }
106 |
107 | .form-container a.nav-link {
108 | background-color: var(--gray100);
109 | border-bottom: 1px solid var(--gray300);
110 | border-radius: 0;
111 | font-weight: bold;
112 | }
113 |
114 | .form-container li.nav-item {
115 | width: 50%;
116 | text-align: center;
117 | color: var(--gray500);
118 | }
119 |
120 | .form-control.is-invalid {
121 | border-color: var(--danger) !important;
122 | background-image: none;
123 | padding-right: 0;
124 | }
125 |
126 | .form-check-label {
127 | margin-bottom: 0.5rem;
128 | }
129 |
130 | form input.form-control-file {
131 | display: none;
132 | }
133 |
134 | form input.form-control {
135 | color: var(--gray100);
136 | border: 0;
137 | border-radius: 0;
138 | border-bottom: 1px solid var(--gray500);
139 | padding: 0;
140 | transition: border-width 0.1s linear, border-color 0.3s linear;
141 | position: relative;
142 | outline: 0;
143 | background-color: transparent;
144 | }
145 |
146 | .form-control:disabled,
147 | .form-control[readonly] {
148 | background-color: transparent;
149 | color: var(--gray100);
150 | border-bottom: 1px solid var(--gray00);
151 | cursor: not-allowed;
152 | }
153 |
154 | form input[type='text']:focus,
155 | form input[type='password']:focus,
156 | form input[type='tel']:focus {
157 | color: var(--gray100);
158 | background-color: transparent;
159 | box-shadow: 0 0 5px rgba(0, 0, 0, 0) !important;
160 | border: none;
161 | border-bottom: 1px solid var(--white);
162 | }
163 |
164 | form input[type='text']::placeholder,
165 | form input[type='password']::placeholder,
166 | form input[type='tel']::placeholder {
167 | color: var(--gray100);
168 | }
169 |
170 | .release-text {
171 | color: var(--gray300);
172 | padding-left: 5px;
173 | font-size: 10px;
174 | }
175 |
176 | .text-fade-in {
177 | -webkit-animation: fadein 1s; /* Safari, Chrome and Opera > 12.1 */
178 | -moz-animation: fadein 1s; /* Firefox < 16 */
179 | animation: fadein 1s;
180 | }
181 |
182 | @keyframes fadein {
183 | from {
184 | opacity: 0;
185 | }
186 |
187 | to {
188 | opacity: 1;
189 | }
190 | }
191 |
192 | /* Firefox < 16 */
193 | @-moz-keyframes fadein {
194 | from {
195 | opacity: 0;
196 | }
197 |
198 | to {
199 | opacity: 1;
200 | }
201 | }
202 |
203 | /* Safari, Chrome and Opera > 12.1 */
204 | @-webkit-keyframes fadein {
205 | from {
206 | opacity: 0;
207 | }
208 |
209 | to {
210 | opacity: 1;
211 | }
212 | }
213 |
214 | .rc-steps-small .rc-steps-item-title {
215 | color: var(--white);
216 | }
217 |
218 | .rc-steps-item-wait .rc-steps-item-icon {
219 | border-color: var(--gray500);
220 | background-color: var(--gray500);
221 | }
222 |
223 | .rc-steps-item-wait .rc-steps-item-icon > .rc-steps-icon {
224 | color: var(--white);
225 | }
226 |
227 | .rc-steps-item-process .rc-steps-item-icon {
228 | font-weight: bold;
229 | color: var(--white);
230 | background-color: var(--gray500);
231 | border-color: var(--gray500);
232 | }
233 |
234 | .rc-steps-item-finish .rc-steps-item-icon {
235 | border-color: var(--gray500);
236 | background-color: var(--gray500);
237 | font-weight: bold;
238 | }
239 |
240 | .rc-steps-item-process .rc-steps-item-title::after {
241 | background-color: var(--gray500);
242 | }
243 |
244 | .rc-steps-item-finish .rc-steps-item-title::after {
245 | background-color: var(--success);
246 | }
247 |
248 | div.recaptcha {
249 | margin: 0 auto;
250 | width: 304px;
251 | }
252 |
253 | .cursor-pointer {
254 | cursor: pointer;
255 | }
256 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 |
4 | import 'bootstrap/dist/css/bootstrap.min.css';
5 | import 'rc-steps/assets/index.css';
6 | import './index.css';
7 |
8 | import { RouterNode } from './routes';
9 |
10 | import reportWebVitals from './reportWebVitals';
11 |
12 | ReactDOM.render(
13 |
14 |
15 | ,
16 | document.getElementById('root')
17 | );
18 |
19 | // If you want to start measuring performance in your app, pass a function
20 | // to log results (for example: reportWebVitals(console.log))
21 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
22 | reportWebVitals();
23 |
--------------------------------------------------------------------------------
/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/routes.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { Suspense, lazy } from 'react';
18 | import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
19 |
20 | import Home from './containers/home';
21 | import Spinner from './components/spinner';
22 |
23 | import { ZilProvider, ZilContext } from './contexts/zil-context';
24 |
25 | export const paths = {
26 | faucet: '/faucet',
27 | send: '/send',
28 | generate: '/generate',
29 | home: '/home',
30 | };
31 |
32 | const Fallback = () => (
33 |
34 |
35 |
36 | );
37 |
38 | export const RouterNode = () => (
39 |
40 |
41 | {(zilContext) => {
42 | if (zilContext.curNetwork === undefined) {
43 | return (
44 |
50 | {'Fetching config'}
51 |
52 | );
53 | }
54 | const RouteList = [
55 | {
56 | path: paths.home,
57 | component: Home,
58 | },
59 | {
60 | path: paths.send,
61 | component: lazy(() => import('./containers/send')),
62 | },
63 | {
64 | path: paths.generate,
65 | component: lazy(() => import('./containers/generate')),
66 | },
67 | {
68 | path: paths.faucet,
69 | component: lazy(() => import('./containers/faucet')),
70 | },
71 | ];
72 |
73 | return (
74 |
75 | }>
76 |
77 | {RouteList.map((curr) => (
78 |
85 | ))}
86 |
87 |
88 |
89 |
90 |
91 | );
92 | }}
93 |
94 |
95 | );
96 |
97 | const PublicRoute = ({ component: Component, zilContext, ...rest }) => {
98 | return } />;
99 | };
100 |
--------------------------------------------------------------------------------
/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/use-async-fn.test.tsx:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { useAsyncFn } from './use-async-fn';
18 | import { render, screen, waitFor, fireEvent } from '@testing-library/react';
19 | import '@testing-library/jest-dom/extend-expect';
20 | import 'whatwg-fetch';
21 |
22 | const Sample = ({ fn, deferred }: any) => {
23 | const { run, init, abort, isInitial, isPending, data, isFulfilled, isRejected, error } =
24 | useAsyncFn({ fn, deferred, x: 'current' });
25 |
26 | return (
27 | <>
28 | {isInitial ? 'true' : 'false'}
29 | {isPending ? 'true' : 'false'}
30 | {isFulfilled ? 'true' : 'false'}
31 | {isRejected ? 'true' : 'false'}
32 | {JSON.stringify(data)}
33 | {error && error.name}
34 | run()} />
35 | run({ x: 'new' })} />
36 | init()} />
37 | abort()} />
38 | >
39 | );
40 | };
41 |
42 | const expectInitial = (screen: any) => {
43 | expect(screen.getByTestId('is-initial')).toHaveTextContent('true');
44 | expect(screen.getByTestId('is-pending')).toHaveTextContent('false');
45 | expect(screen.getByTestId('is-fulfilled')).toHaveTextContent('false');
46 | expect(screen.getByTestId('is-rejected')).toHaveTextContent('false');
47 | expect(screen.getByTestId('data')).toHaveTextContent(``);
48 | expect(screen.getByTestId('error')).toHaveTextContent(``);
49 | };
50 | const expectPending = (screen: any) => {
51 | expect(screen.getByTestId('is-initial')).toHaveTextContent('false');
52 | expect(screen.getByTestId('is-pending')).toHaveTextContent('true');
53 | expect(screen.getByTestId('is-fulfilled')).toHaveTextContent('false');
54 | expect(screen.getByTestId('is-rejected')).toHaveTextContent('false');
55 | expect(screen.getByTestId('data')).toHaveTextContent(``);
56 | expect(screen.getByTestId('error')).toHaveTextContent(``);
57 | };
58 | const expectFulfilledWithData = (screen: any, data: string) => {
59 | expect(screen.getByTestId('is-initial')).toHaveTextContent('false');
60 | expect(screen.getByTestId('is-pending')).toHaveTextContent('false');
61 | expect(screen.getByTestId('is-fulfilled')).toHaveTextContent('true');
62 | expect(screen.getByTestId('is-rejected')).toHaveTextContent('false');
63 | expect(screen.getByTestId('data')).toHaveTextContent(data);
64 | expect(screen.getByTestId('error')).toHaveTextContent(``);
65 | };
66 | const expectRejected = (screen: any) => {
67 | expect(screen.getByTestId('is-initial')).toHaveTextContent('false');
68 | expect(screen.getByTestId('is-pending')).toHaveTextContent('false');
69 | expect(screen.getByTestId('is-fulfilled')).toHaveTextContent('false');
70 | expect(screen.getByTestId('is-rejected')).toHaveTextContent('true');
71 | expect(screen.getByTestId('data')).toHaveTextContent(``);
72 | expect(screen.getByTestId('error')).toHaveTextContent('Error');
73 | };
74 | const expectAborted = (screen: any) => {
75 | expect(screen.getByTestId('is-initial')).toHaveTextContent('false');
76 | expect(screen.getByTestId('is-pending')).toHaveTextContent('false');
77 | expect(screen.getByTestId('is-fulfilled')).toHaveTextContent('false');
78 | expect(screen.getByTestId('is-rejected')).toHaveTextContent('true');
79 | expect(screen.getByTestId('data')).toHaveTextContent(``);
80 | expect(screen.getByTestId('error')).toHaveTextContent('AbortError');
81 | };
82 |
83 | test('run promise with resolved value', async () => {
84 | const fn = jest.fn().mockResolvedValue({ statusCode: 200 });
85 | render( );
86 | expect(fn).toHaveBeenCalledWith({
87 | args: { x: 'current' },
88 | signal: new AbortController().signal,
89 | });
90 | expectPending(screen);
91 | await waitFor(() => expectFulfilledWithData(screen, `{"statusCode":200}`));
92 | });
93 |
94 | test('run promise with new args', async () => {
95 | const fn = jest.fn().mockResolvedValue({ statusCode: 200 });
96 | render( );
97 |
98 | expect(fn).toHaveBeenCalledWith({
99 | args: { x: 'current' },
100 | signal: new AbortController().signal,
101 | });
102 | expectPending(screen);
103 | await waitFor(() => expectFulfilledWithData(screen, `{"statusCode":200}`));
104 |
105 | await fireEvent.click(screen.getByTestId('run-with-new-args'));
106 | expect(fn.mock.calls[fn.mock.calls.length - 1]).toEqual([
107 | {
108 | args: { x: 'new' },
109 | signal: new AbortController().signal,
110 | },
111 | ]);
112 |
113 | expectPending(screen);
114 | await waitFor(() => expectFulfilledWithData(screen, `{"statusCode":200}`));
115 | });
116 |
117 | test('run promise with rejected value', async () => {
118 | const fn = jest.fn().mockRejectedValue(new Error());
119 | render( );
120 | expect(fn).toHaveBeenCalledWith({
121 | args: { x: 'current' },
122 | signal: new AbortController().signal,
123 | });
124 | expectPending(screen);
125 | await waitFor(() => expectRejected(screen));
126 | });
127 |
128 | test('run promise and abort', async () => {
129 | const fn = async ({ signal }: any) =>
130 | await fetch(`https://www.google.com/`, {
131 | signal,
132 | method: 'GET',
133 | });
134 | render( );
135 | expectPending(screen);
136 | await fireEvent.click(screen.getByTestId('abort'));
137 | await waitFor(() => expectAborted(screen));
138 | });
139 |
140 | test('run promise and init', async () => {
141 | const fn = jest.fn().mockResolvedValue({ statusCode: 200 });
142 | render( );
143 | expectPending(screen);
144 | await fireEvent.click(screen.getByTestId('init'));
145 | await waitFor(() => expectInitial(screen));
146 | });
147 |
148 | test('run defer fn with resolved value', async () => {
149 | const fn = jest.fn().mockResolvedValue({ statusCode: 200 });
150 | render( );
151 | expect(fn).not.toHaveBeenCalled();
152 | expectInitial(screen);
153 | await fireEvent.click(screen.getByTestId('run-with-current-args'));
154 | expect(fn).toHaveBeenCalledWith({
155 | args: { x: 'current' },
156 | signal: new AbortController().signal,
157 | });
158 | expectPending(screen);
159 | await waitFor(() => screen.getByTestId('data'));
160 | expectFulfilledWithData(screen, `{"statusCode":200}`);
161 | });
162 |
163 | test('run defer fn with rejected value', async () => {
164 | const fn = jest.fn().mockRejectedValue(new Error());
165 | render( );
166 | expect(fn).not.toHaveBeenCalled();
167 | expectInitial(screen);
168 | await fireEvent.click(screen.getByTestId('run-with-current-args'));
169 | expect(fn).toHaveBeenCalledWith({
170 | args: { x: 'current' },
171 | signal: new AbortController().signal,
172 | });
173 | expectPending(screen);
174 | await waitFor(() => expectRejected(screen));
175 | });
176 |
177 | test('run defer fn with new args', async () => {
178 | const fn = jest.fn().mockResolvedValue({ statusCode: 200 });
179 | render( );
180 |
181 | expect(fn).not.toHaveBeenCalled();
182 |
183 | expectInitial(screen);
184 |
185 | await fireEvent.click(screen.getByTestId('run-with-new-args'));
186 |
187 | expect(fn).toHaveBeenCalledWith({
188 | args: { x: 'new' },
189 | signal: new AbortController().signal,
190 | });
191 |
192 | expectPending(screen);
193 | await waitFor(() => expectFulfilledWithData(screen, `{"statusCode":200}`));
194 |
195 | expect(fn).toHaveBeenCalledWith({
196 | args: { x: 'new' },
197 | signal: new AbortController().signal,
198 | });
199 | });
200 |
201 | test('run defer fn and abort', async () => {
202 | const fn = async ({ signal }: any) =>
203 | await fetch(`https://www.google.com/`, {
204 | signal,
205 | method: 'GET',
206 | });
207 | render( );
208 | expectInitial(screen);
209 |
210 | await fireEvent.click(screen.getByTestId('run-with-current-args'));
211 | await waitFor(() => expectPending(screen));
212 |
213 | await fireEvent.click(screen.getByTestId('abort'));
214 | await waitFor(() => expectAborted(screen));
215 | });
216 |
217 | test('run defer fn and init', async () => {
218 | const fn = jest.fn().mockResolvedValue({ statusCode: 200 });
219 | render( );
220 | expect(fn).not.toHaveBeenCalled();
221 | expectInitial(screen);
222 |
223 | await fireEvent.click(screen.getByTestId('run-with-current-args'));
224 | await waitFor(() => expectPending(screen));
225 |
226 | await fireEvent.click(screen.getByTestId('init'));
227 | await waitFor(() => expectInitial(screen));
228 | });
229 |
--------------------------------------------------------------------------------
/src/use-async-fn.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 |
17 | import { useEffect, useReducer, useRef, useCallback } from 'react';
18 |
19 | export enum statusTypes {
20 | Initial = 'Initial',
21 | Pending = 'Pending',
22 | Fulfilled = 'Fulfilled',
23 | Rejected = 'Rejected',
24 | }
25 |
26 | enum actionTypes {
27 | Init = 'Init',
28 | Run = 'Run',
29 | Abort = 'Abort',
30 | Fulfill = 'Fulfill',
31 | Reject = 'Reject',
32 | }
33 |
34 | interface Args {
35 | type: actionTypes;
36 | payload: any;
37 | }
38 |
39 | const getStatusProps = (status: statusTypes) => ({
40 | status,
41 | isInitial: status === statusTypes.Initial,
42 | isPending: status === statusTypes.Pending,
43 | isFulfilled: status === statusTypes.Fulfilled,
44 | isRejected: status === statusTypes.Rejected,
45 | });
46 |
47 | const initialize = () => ({
48 | data: undefined,
49 | error: undefined,
50 | ...getStatusProps(statusTypes.Initial),
51 | });
52 |
53 | const reducer = (state: any, args: Args) => {
54 | const { type, payload } = args;
55 | switch (type) {
56 | case actionTypes.Init:
57 | return initialize();
58 | case actionTypes.Run:
59 | return {
60 | ...state,
61 | data: undefined,
62 | error: undefined,
63 | ...getStatusProps(statusTypes.Pending),
64 | };
65 | case actionTypes.Fulfill:
66 | return {
67 | ...state,
68 | data: payload,
69 | error: undefined,
70 | ...getStatusProps(statusTypes.Fulfilled),
71 | };
72 | case actionTypes.Reject:
73 | return {
74 | ...state,
75 | data: undefined,
76 | error: payload,
77 | ...getStatusProps(statusTypes.Rejected),
78 | };
79 | case actionTypes.Abort:
80 | return {
81 | data: undefined,
82 | error: payload,
83 | ...getStatusProps(statusTypes.Rejected),
84 | };
85 | }
86 | };
87 |
88 | export interface UseAsyncFn {
89 | (...args: any[]): any;
90 | }
91 |
92 | export const useAsyncFn: UseAsyncFn = ({ fn, deferred = false, ...args }) => {
93 | const [state, dispatch] = useReducer(reducer, initialize());
94 | const asyncFn = fn;
95 | const isMountedRef = useRef(true);
96 | const abortControllerRef = useRef();
97 | const argsRef = useRef(args);
98 | const init = (): void => {
99 | isMountedRef.current && dispatch({ type: actionTypes.Init, payload: undefined });
100 | };
101 |
102 | const run = useCallback(
103 | async (newArgs?: any) => {
104 | if (newArgs) {
105 | argsRef.current = newArgs;
106 | }
107 |
108 | abortControllerRef.current = new AbortController();
109 |
110 | isMountedRef.current && dispatch({ type: actionTypes.Run, payload: undefined });
111 | try {
112 | const signal: AbortSignal | undefined =
113 | abortControllerRef.current && abortControllerRef.current.signal;
114 | const payload = await asyncFn({ args: argsRef.current, signal });
115 | isMountedRef.current && dispatch({ type: actionTypes.Fulfill, payload });
116 | } catch (error: any) {
117 | const curType =
118 | error && error.name === 'AbortError' ? actionTypes.Abort : actionTypes.Reject;
119 |
120 | isMountedRef.current && dispatch({ type: curType, payload: error });
121 | }
122 | },
123 | [asyncFn]
124 | );
125 |
126 | const abort = (): void => {
127 | if (abortControllerRef.current) {
128 | abortControllerRef.current.abort();
129 | }
130 | };
131 |
132 | useEffect(
133 | () => () => {
134 | isMountedRef.current = false;
135 | },
136 | []
137 | );
138 | useEffect(() => {
139 | if (!deferred) {
140 | run();
141 | }
142 | return abort;
143 | }, [fn, run]);
144 | return { ...state, run, init, abort };
145 | };
146 |
--------------------------------------------------------------------------------
/src/utils.spec.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 | import { formatSendAmountInZil } from './utils';
17 |
18 | describe('formatSendAmountInZil tests', () => {
19 | describe('basic tests', () => {
20 | it('int amount and int balance', () => {
21 | const result = formatSendAmountInZil('100', '200', '0.001');
22 | expect(result).toBe('100');
23 | });
24 |
25 | it('float amount and int balance', () => {
26 | const result = formatSendAmountInZil('98.999', '99', '0.001');
27 | expect(result).toBe('98.999');
28 | });
29 |
30 | it('int amount and float balance', () => {
31 | const result = formatSendAmountInZil('99', '99.999', '0.001');
32 | expect(result).toBe('99');
33 | });
34 |
35 | it('float amount and float balance', () => {
36 | const result = formatSendAmountInZil('99.000', '99.999', '0.001');
37 | expect(result).toBe('99');
38 | });
39 |
40 | it('min value test', () => {
41 | const result = formatSendAmountInZil('0.000', '100.000', '0.001');
42 | expect(result).toBe('0.001');
43 | });
44 |
45 | it('max value test', () => {
46 | const result = formatSendAmountInZil('1000.000', '100.002', '0.001');
47 | expect(result).toBe('100.001');
48 | });
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2018 - present Zilliqa Research Pte. Ltd.
3 | *
4 | * This program is free software: you can redistribute it and/or modify it under the
5 | * terms of the GNU General Public License as published by the Free Software
6 | * Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY
10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 | * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with
14 | * this program. If not, see .
15 | */
16 | import { BN, units } from '@zilliqa-js/util';
17 |
18 | export const setValIfWholeNum =
19 | (fn) =>
20 | (e: React.ChangeEvent): void => {
21 | e.preventDefault();
22 | const value = e.target.value;
23 | const isWholeNumber = /^\d*\.?\d*$/.test(value) && !isNaN(Number(value));
24 | const isEmptyString = value === '';
25 | return isEmptyString || isWholeNumber ? fn(value) : undefined;
26 | };
27 |
28 | export const getInputValidationState = (key: string, value: string, testVal: RegExp | boolean) => {
29 | const isInvalid: boolean = typeof testVal === 'boolean' ? testVal : testVal.test(value);
30 | const keyValid = key + 'Valid';
31 | const keyInvalid = key + 'Invalid';
32 | const state = {};
33 | if (!value) {
34 | state[keyValid] = false;
35 | state[keyInvalid] = false;
36 | return state;
37 | }
38 | if (isInvalid) {
39 | state[keyValid] = true;
40 | state[keyInvalid] = false;
41 | return state;
42 | } else {
43 | state[keyValid] = false;
44 | state[keyInvalid] = true;
45 | return state;
46 | }
47 | };
48 |
49 | export const downloadObjectAsJson = (exportObj, exportName) => {
50 | const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(exportObj));
51 | const downloadAnchorNode = document.createElement('a');
52 | downloadAnchorNode.setAttribute('href', dataStr);
53 | downloadAnchorNode.setAttribute('download', exportName + '.json');
54 | document.body.appendChild(downloadAnchorNode); // required for firefox
55 | downloadAnchorNode.click();
56 | downloadAnchorNode.remove();
57 | };
58 |
59 | export const formatSendAmountInZil = (
60 | amountInZil: string,
61 | balanceInZil: string,
62 | minGasPriceInZil: string
63 | ): string => {
64 | const amountInQaBN: BN = units.toQa(amountInZil, units.Units.Zil);
65 | const balanceInQaBN: BN = units.toQa(balanceInZil, units.Units.Zil);
66 | const minGasPriceInQaBN: BN = units.toQa(minGasPriceInZil, units.Units.Zil);
67 |
68 | const maxAmountInQaBN = balanceInQaBN.sub(minGasPriceInQaBN);
69 |
70 | if (amountInQaBN.lte(minGasPriceInQaBN)) {
71 | return units.fromQa(minGasPriceInQaBN, units.Units.Zil).toString();
72 | } else if (amountInQaBN.gt(maxAmountInQaBN)) {
73 | return units.fromQa(maxAmountInQaBN, units.Units.Zil).toString();
74 | } else {
75 | return units.fromQa(amountInQaBN, units.Units.Zil).toString();
76 | }
77 | };
78 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "noImplicitAny": false,
15 | "forceConsistentCasingInFileNames": true,
16 | "noFallthroughCasesInSwitch": true,
17 | "module": "esnext",
18 | "moduleResolution": "node",
19 | "resolveJsonModule": true,
20 | "isolatedModules": true,
21 | "noEmit": true,
22 | "jsx": "react-jsx"
23 | },
24 | "include": [
25 | "src"
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------