├── .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 | 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 |
e.preventDefault()}> 140 | 141 |

{description}

142 |
143 | 144 | {'Keystore File'} 145 | 146 |
147 | 152 | 159 |

{filename ? {filename} : null}

160 |
161 | 166 | 181 | {'invalid passphrase'} 182 | {'valid passphrase'} 183 |
184 | 185 |
186 | 187 | 190 | 191 |
192 | { 193 |
213 |
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 |
7 |
10 |

13 | You can access your wallet with private key. 14 |

15 | 25 | 37 |
40 | invalid private key 41 |
42 |
45 | valid private key 46 |
47 |
48 |
49 |
52 | 71 |
72 |
75 | 85 |
86 |
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 |
e.preventDefault()}> 82 | 83 |

{description}

84 | 89 | 103 | {'invalid private key'} 104 | {'valid private key'} 105 |
106 | 107 |
108 | 109 | 112 | 113 |
114 | { 115 |
130 |
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 | 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 | 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 | 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 | 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 | 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 |
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 | 15 | `; 16 | 17 | exports[`matches the snapshot 2`] = ` 18 | 28 | `; 29 | 30 | exports[`matches the snapshot 3`] = ` 31 | 42 | `; 43 | 44 | exports[`matches the snapshot 4`] = ` 45 | 55 | `; 56 | 57 | exports[`matches the snapshot 5`] = ` 58 | 69 | `; 70 | 71 | exports[`matches the snapshot 6`] = ` 72 | 82 | `; 83 | 84 | exports[`matches the snapshot 7`] = ` 85 | 96 | `; 97 | 98 | exports[`matches the snapshot 8`] = ` 99 | 109 | `; 110 | 111 | exports[`matches the snapshot 9`] = ` 112 | 123 | `; 124 | 125 | exports[`matches the snapshot 10`] = ` 126 | 136 | `; 137 | 138 | exports[`matches the snapshot 11`] = ` 139 | 150 | `; 151 | 152 | exports[`matches the snapshot 12`] = ` 153 | 163 | `; 164 | 165 | exports[`matches the snapshot 13`] = ` 166 | 177 | `; 178 | 179 | exports[`matches the snapshot 14`] = ` 180 | 190 | `; 191 | 192 | exports[`matches the snapshot 15`] = ` 193 | 204 | `; 205 | 206 | exports[`matches the snapshot 16`] = ` 207 | 217 | `; 218 | 219 | exports[`matches the snapshot 17`] = ` 220 | 231 | `; 232 | 233 | exports[`matches the snapshot 18`] = ` 234 | 244 | `; 245 | 246 | exports[`matches the snapshot 19`] = ` 247 | 260 | `; 261 | 262 | exports[`matches the snapshot 20`] = ` 263 | 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 | 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 | 24 | 111 | 112 | 113 | 114 | `; 115 | 116 | exports[`matches the snapshot 2`] = ` 117 |
118 |
119 | 230 |
231 |
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 | 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 |
e.preventDefault()}> 103 | 104 | 109 | 121 | {'invalid address'} 122 | {'valid address'} 123 | 124 |
125 | 126 | 131 | 143 | 144 | 145 | Gas Price:{' '} 146 | {isUpdatingMinGasPrice ? 'loading...' : `${minGasPriceInZil} ZIL`} 147 | 148 |
149 |
150 |
157 | {isBalanceInsufficient && !isUpdatingBalance ? ( 158 |

159 | 160 | {'Your balance is not sufficient to send transaction.'} 161 |
162 | {`Minimum Gas Price: ${minGasPriceInZil} ZIL`} 163 |
164 |

165 | ) : null} 166 |
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 |
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 |
e.preventDefault()}> 285 | 286 | 292 | 293 |
294 |
307 |
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 |
7 |
10 |
11 | `; 12 | 13 | exports[`matches the snapshot when loading 1`] = ` 14 |
17 |
20 |
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 |
27 |
28 |
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 |
77 | 78 | 83 | 95 | {'invalid address'} 96 | {'valid address'} 97 | 98 |
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 |