diff --git a/README.md b/README.md index 4c4e624..93a8c48 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ npx hardhat scripts/deploy.js --network [NETWORK] 5. Adding support in the frontend: - - Open the `constants.js` file in the `frontend/utils` directory. + - Open the `constants.ts` file in the `frontend/utils` directory. - Include the chain information and Disperse contract address by adding the following code snippet in `supportedChains` array: ```javascript { diff --git a/frontend/index.html b/frontend/index.html index 7bfda6a..2da6329 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -12,6 +12,6 @@
- + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 69b4d9a..ef8b00c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -14,10 +14,13 @@ "web3modal": "^1.9.4" }, "devDependencies": { + "@types/react": "^18.2.16", + "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^1.0.0", "autoprefixer": "^10.4.0", "postcss": "^8.4.4", "tailwindcss": "^2.2.19", + "typescript": "^5.1.6", "vite": "^2.6.4" } }, @@ -1173,6 +1176,38 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "node_modules/@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "node_modules/@types/react": { + "version": "18.2.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.16.tgz", + "integrity": "sha512-LLFWr12ZhBJ4YVw7neWLe6Pk7Ey5R9OCydfuMsz1L8bZxzaawJj2p06Q8/EFEHDeTBQNFLF62X+CG7B2zIyu0Q==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true + }, "node_modules/@vitejs/plugin-react": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.1.0.tgz", @@ -1588,6 +1623,12 @@ "node": ">=4" } }, + "node_modules/csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dev": true + }, "node_modules/debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -3282,6 +3323,19 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -4154,6 +4208,38 @@ "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@types/prop-types": { + "version": "15.7.5", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", + "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==", + "dev": true + }, + "@types/react": { + "version": "18.2.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.16.tgz", + "integrity": "sha512-LLFWr12ZhBJ4YVw7neWLe6Pk7Ey5R9OCydfuMsz1L8bZxzaawJj2p06Q8/EFEHDeTBQNFLF62X+CG7B2zIyu0Q==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "@types/scheduler": "*", + "csstype": "^3.0.2" + } + }, + "@types/react-dom": { + "version": "18.2.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.7.tgz", + "integrity": "sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==", + "dev": true, + "requires": { + "@types/react": "*" + } + }, + "@types/scheduler": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", + "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==", + "dev": true + }, "@vitejs/plugin-react": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.1.0.tgz", @@ -4481,6 +4567,12 @@ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true }, + "csstype": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dev": true + }, "debug": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", @@ -5715,6 +5807,12 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 666dee5..1dafc4f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "scripts": { "dev": "vite", - "build": "vite build", + "build": "tsc && vite build", "serve": "vite preview" }, "dependencies": { @@ -13,10 +13,13 @@ "web3modal": "^1.9.4" }, "devDependencies": { + "@types/react": "^18.2.16", + "@types/react-dom": "^18.2.7", "@vitejs/plugin-react": "^1.0.0", "autoprefixer": "^10.4.0", "postcss": "^8.4.4", "tailwindcss": "^2.2.19", + "typescript": "^5.1.6", "vite": "^2.6.4" } } diff --git a/frontend/src/App.jsx b/frontend/src/App.tsx similarity index 91% rename from frontend/src/App.jsx rename to frontend/src/App.tsx index 86bcdc9..d35c3de 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.tsx @@ -8,15 +8,15 @@ import WalletInfo from "./components/WalletInfo"; import Warn from "./components/Warn"; import Web3Modal from "web3modal"; import Connect from "./components/Connect"; -import { initState, reducer } from "./reducers/index"; +import { initNetworkContextType, initState, reducer } from "./reducers"; import { getNetworkInfo, isChainSupported } from "./utils"; -export const NetworkContext = createContext(); +export const NetworkContext = createContext(initNetworkContextType); function App() { - const [isMetamaskConnected, setIsMetamaskConnected] = useState(); - const [isMetamaskInstalled, setIsMetamaskInstalled] = useState(); - const [address, setAddress] = useState(null); + const [isMetamaskConnected, setIsMetamaskConnected] = useState(false); + const [isMetamaskInstalled, setIsMetamaskInstalled] = useState(false); + const [address, setAddress] = useState(null); const [isLoading, setIsLoading] = useState(true); const [state, dispatch] = useReducer(reducer, initState); @@ -57,6 +57,7 @@ function App() { }; const checkAccountConnected = async () => { + const { ethereum } = window; const provider = new ethers.providers.Web3Provider(ethereum); const accounts = await provider.listAccounts(); if (!accounts.length) { @@ -84,7 +85,7 @@ function App() { if (ethereum) { setIsMetamaskInstalled(true); checkAccountConnected(); - fetchNetworkDetails(ethereum); + fetchNetworkDetails(); } else { setIsMetamaskInstalled(false); } diff --git a/frontend/src/components/Confirm.jsx b/frontend/src/components/Confirm.tsx similarity index 85% rename from frontend/src/components/Confirm.jsx rename to frontend/src/components/Confirm.tsx index 831ba2e..7443e93 100644 --- a/frontend/src/components/Confirm.jsx +++ b/frontend/src/components/Confirm.tsx @@ -1,7 +1,20 @@ import { ethers } from "ethers"; -import { useState, useEffect, useContext } from "react"; +import { useEffect, useState } from "react"; +import { RecipientInfo } from "../types/Recipient"; +import { TxStatus } from "../types/Transaction"; import Status from "./Status"; +type ConfirmProps = { + recipientsData: RecipientInfo[]; + total: ethers.BigNumber | null; + tokenBalance: string | null; + remaining: string | null; + approve: () => Promise; + disperse: () => Promise; + txStatus: TxStatus | null; + approveStatus: TxStatus | null; +}; + const Confirm = ({ recipientsData, total, @@ -11,7 +24,7 @@ const Confirm = ({ disperse, txStatus, approveStatus, -}) => { +}: ConfirmProps) => { const [isDisabled, setIsDisabled] = useState(false); useEffect(() => { @@ -31,8 +44,8 @@ const Confirm = ({ {recipientsData.length > 0 && - recipientsData.map((recipient) => ( -
  • + recipientsData.map((recipient, index) => ( +
  • {recipient.address}

    {/* TODO: Add Horizontal line here */} diff --git a/frontend/src/components/ConfirmEther.jsx b/frontend/src/components/ConfirmEther.tsx similarity index 87% rename from frontend/src/components/ConfirmEther.jsx rename to frontend/src/components/ConfirmEther.tsx index b8e76b2..b259fdc 100644 --- a/frontend/src/components/ConfirmEther.jsx +++ b/frontend/src/components/ConfirmEther.tsx @@ -1,8 +1,18 @@ import { ethers } from "ethers"; -import { useContext, useEffect, useState } from "react"; -import { NetworkContext } from "../App"; +import { useEffect, useState } from "react"; +import { RecipientInfo } from "../types/Recipient"; +import { TxStatus } from "../types/Transaction"; import Status from "./Status"; +type ConfirmEtherProps = { + recipientsData: RecipientInfo[]; + total: ethers.BigNumber | null; + tokenBalance: string | null; + remaining: string | null; + disperse: () => Promise; + txStatus: TxStatus | null; +}; + const ConfirmEther = ({ recipientsData, total, @@ -10,9 +20,8 @@ const ConfirmEther = ({ remaining, disperse, txStatus, -}) => { +}: ConfirmEtherProps) => { const [isDisabled, setIsDisabled] = useState(false); - const { chainId } = useContext(NetworkContext); useEffect(() => { if (total && tokenBalance) { diff --git a/frontend/src/components/Connect.jsx b/frontend/src/components/Connect.tsx similarity index 81% rename from frontend/src/components/Connect.jsx rename to frontend/src/components/Connect.tsx index dbf0d1b..2321c72 100644 --- a/frontend/src/components/Connect.jsx +++ b/frontend/src/components/Connect.tsx @@ -1,4 +1,8 @@ -const Connect = ({ connect }) => { +type ConnectProps = { + connect: () => void; +}; + +const Connect = ({ connect }: ConnectProps) => { return (

    connect to wallet

    diff --git a/frontend/src/components/Ether.jsx b/frontend/src/components/Ether.tsx similarity index 82% rename from frontend/src/components/Ether.jsx rename to frontend/src/components/Ether.tsx index bf03c7b..872f40c 100644 --- a/frontend/src/components/Ether.jsx +++ b/frontend/src/components/Ether.tsx @@ -6,15 +6,21 @@ import { getNetworkInfo, parseText } from "../utils/index"; import Recipients from "./Recipients"; import ConfirmEther from "./ConfirmEther"; import { NetworkContext } from "../App"; +import { TxStatus } from "../types/Transaction"; +import { RecipientInfo } from "../types/Recipient"; -const Ether = ({ address }) => { - const [ethBalance, setEthBalance] = useState(null); +type EtherProps = { + address: string; +}; + +const Ether = ({ address }: EtherProps) => { + const [ethBalance, setEthBalance] = useState(null); const [textValue, setTextValue] = useState(""); - const [total, setTotal] = useState(null); - const [recipientsData, setRecipientsData] = useState([]); - const [remaining, setRemaining] = useState(null); + const [total, setTotal] = useState(null); + const [recipientsData, setRecipientsData] = useState([]); + const [remaining, setRemaining] = useState(null); const { chainId } = useContext(NetworkContext); - const [txStatus, setTxStatus] = useState(null); + const [txStatus, setTxStatus] = useState(null); const networkInfo = getNetworkInfo(chainId); const disperseAddress = networkInfo?.disperseAddress; @@ -22,8 +28,8 @@ const Ether = ({ address }) => { const { ethereum } = window; if (!ethBalance) { const provider = new ethers.providers.Web3Provider(ethereum); - let ethBalance = await provider.getBalance(address); - ethBalance = ethers.utils.formatEther(ethBalance); + const balance = await provider.getBalance(address); + const ethBalance = ethers.utils.formatEther(balance); setEthBalance(ethBalance); } }; diff --git a/frontend/src/components/Headers.jsx b/frontend/src/components/Headers.tsx similarity index 89% rename from frontend/src/components/Headers.jsx rename to frontend/src/components/Headers.tsx index 09bb1c7..dd0bda2 100644 --- a/frontend/src/components/Headers.jsx +++ b/frontend/src/components/Headers.tsx @@ -2,8 +2,13 @@ import { useContext } from "react"; import { NetworkContext } from "../App"; import EthereumSVG from "../assets/ethereum.svg"; -const Header = ({ address }) => { +type HeaderProps = { + address: string | null; +}; + +const Header = ({ address }: HeaderProps) => { const networkContext = useContext(NetworkContext); + return (
    diff --git a/frontend/src/components/Payment.jsx b/frontend/src/components/Payment.tsx similarity index 86% rename from frontend/src/components/Payment.jsx rename to frontend/src/components/Payment.tsx index 520800b..7583879 100644 --- a/frontend/src/components/Payment.jsx +++ b/frontend/src/components/Payment.tsx @@ -7,35 +7,48 @@ import Recipients from "./Recipients"; import { NetworkContext } from "../App"; import { getNetworkInfo, parseText, getWarnMessage } from "../utils/index"; import Ether from "./Ether"; +import { TxStatus } from "../types/Transaction"; +import { RecipientInfo } from "../types/Recipient"; -const Payment = ({ address }) => { - const defaultTokenDetails = { +type TokenDetails = { + name: string | null; + symbol: string | null; + balance: string | null; +}; + +type PaymentProps = { + address: string; +}; + +const Payment = ({ address }: PaymentProps) => { + const defaultTokenDetails: TokenDetails = { name: null, symbol: null, balance: null, }; const { chainId } = useContext(NetworkContext); - const [currentLink, setCurrentLink] = useState(null); - const [ethBalance, setEthBalance] = useState(null); + const [currentLink, setCurrentLink] = useState(null); + const [ethBalance, setEthBalance] = useState(null); const [tokenAddress, setTokenAddress] = useState(""); - const [tokenDetails, setTokenDetails] = useState(defaultTokenDetails); + const [tokenDetails, setTokenDetails] = + useState(defaultTokenDetails); const [textValue, setTextValue] = useState(""); const [isTokenLoading, setIsTokenLoading] = useState(false); - const [recipientsData, setRecipientsData] = useState([]); - const [total, setTotal] = useState(null); - const [remaining, setRemaining] = useState(null); - const [warn, setWarn] = useState(null); - const [txStatus, setTxStatus] = useState(null); - const [approveStatus, setApproveStatus] = useState(null); + const [recipientsData, setRecipientsData] = useState([]); + const [total, setTotal] = useState(null); + const [remaining, setRemaining] = useState(null); + const [warn, setWarn] = useState(null); + const [txStatus, setTxStatus] = useState(null); + const [approveStatus, setApproveStatus] = useState(null); const [isInvalidToken, setIsInvalidToken] = useState(false); const networkInfo = getNetworkInfo(chainId); const disperseAddress = networkInfo?.disperseAddress; - const getEthBalance = async (ethereum) => { + const getEthBalance = async (ethereum: any) => { if (!ethBalance) { const provider = new ethers.providers.Web3Provider(ethereum); - let ethBalance = await provider.getBalance(address); - ethBalance = ethers.utils.formatEther(ethBalance); + const balance = await provider.getBalance(address); + const ethBalance = ethers.utils.formatEther(balance); setEthBalance(ethBalance); } }; diff --git a/frontend/src/components/Recipients.jsx b/frontend/src/components/Recipients.tsx similarity index 78% rename from frontend/src/components/Recipients.jsx rename to frontend/src/components/Recipients.tsx index 011e7b7..f7aafe0 100644 --- a/frontend/src/components/Recipients.jsx +++ b/frontend/src/components/Recipients.tsx @@ -1,4 +1,14 @@ -const Recipients = ({ tokenSymbol, textValue, setTextValue }) => { +type RecipientsProps = { + tokenSymbol: string | null; + textValue: string; + setTextValue: React.Dispatch>; +}; + +const Recipients = ({ + tokenSymbol, + textValue, + setTextValue, +}: RecipientsProps) => { return (

    recipients and amounts

    diff --git a/frontend/src/components/Status.jsx b/frontend/src/components/Status.tsx similarity index 84% rename from frontend/src/components/Status.jsx rename to frontend/src/components/Status.tsx index d45e5ee..f7da7dc 100644 --- a/frontend/src/components/Status.jsx +++ b/frontend/src/components/Status.tsx @@ -1,8 +1,13 @@ import { useContext } from "react"; import { NetworkContext } from "../App"; import { getNetworkInfo } from "../utils"; +import { TxStatus } from "../types/Transaction"; -const Status = ({ txnStatus }) => { +type StatusProps = { + txnStatus: TxStatus; +}; + +const Status = ({ txnStatus }: StatusProps) => { const { chainId } = useContext(NetworkContext); const networkInfo = getNetworkInfo(chainId); diff --git a/frontend/src/components/WalletInfo.jsx b/frontend/src/components/WalletInfo.tsx similarity index 68% rename from frontend/src/components/WalletInfo.jsx rename to frontend/src/components/WalletInfo.tsx index f5007c0..02237eb 100644 --- a/frontend/src/components/WalletInfo.jsx +++ b/frontend/src/components/WalletInfo.tsx @@ -1,4 +1,8 @@ -const WalletInfo = ({ address, provider }) => { +type WalletInfoProps = { + address: string; +}; + +const WalletInfo = ({ address }: WalletInfoProps) => { return (

    connect to wallet

    diff --git a/frontend/src/components/Warn.jsx b/frontend/src/components/Warn.tsx similarity index 100% rename from frontend/src/components/Warn.jsx rename to frontend/src/components/Warn.tsx diff --git a/frontend/src/global.d.ts b/frontend/src/global.d.ts new file mode 100644 index 0000000..48d8239 --- /dev/null +++ b/frontend/src/global.d.ts @@ -0,0 +1,7 @@ +import { ethers } from "ethers"; + +declare global { + interface Window { + ethereum?: ethers.providers.ExternalProvider; + } +} diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx deleted file mode 100644 index 606a3cf..0000000 --- a/frontend/src/main.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react' -import ReactDOM from 'react-dom' -import './index.css' -import App from './App' - -ReactDOM.render( - - - , - document.getElementById('root') -) diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..846ed2c --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,11 @@ +import React from "react"; +import ReactDOM from "react-dom"; +import "./index.css"; +import App from "./App"; + +ReactDOM.render( + + + , + document.getElementById("root") +); diff --git a/frontend/src/reducers/index.js b/frontend/src/reducers/index.js deleted file mode 100644 index 3b974ec..0000000 --- a/frontend/src/reducers/index.js +++ /dev/null @@ -1,23 +0,0 @@ -export const initState = { - chainId: null, - network: null, -}; - -export const reducer = (state, action) => { - switch (action.type) { - case "SET_CHAIN_ID": { - return { - ...state, - chainId: action.payload, - }; - } - case "SET_NETWORK": { - return { - ...state, - network: action.payload, - }; - } - default: - return state; - } -}; diff --git a/frontend/src/reducers/index.ts b/frontend/src/reducers/index.ts new file mode 100644 index 0000000..1709a61 --- /dev/null +++ b/frontend/src/reducers/index.ts @@ -0,0 +1,45 @@ +type StateType = { + chainId: number; + network: string | null; +}; + +export const initState: StateType = { + chainId: 0, + network: null, +}; + +type ReducerActionType = + | { type: "SET_CHAIN_ID"; payload: number } + | { type: "SET_NETWORK"; payload: string | null }; + +export const reducer = ( + state: StateType, + action: ReducerActionType +): StateType => { + switch (action.type) { + case "SET_CHAIN_ID": { + return { + ...state, + chainId: action.payload, + }; + } + case "SET_NETWORK": { + return { + ...state, + network: action.payload, + }; + } + default: + return state; + } +}; + +type NetworkContextType = { + chainId: number; + network: string | null; +}; + +export const initNetworkContextType: NetworkContextType = { + chainId: 0, + network: null, +}; diff --git a/frontend/src/types/Recipient.ts b/frontend/src/types/Recipient.ts new file mode 100644 index 0000000..89e5248 --- /dev/null +++ b/frontend/src/types/Recipient.ts @@ -0,0 +1,6 @@ +import { ethers } from "ethers"; + +export type RecipientInfo = { + address: string; + value: ethers.BigNumber; +}; diff --git a/frontend/src/types/Transaction.ts b/frontend/src/types/Transaction.ts new file mode 100644 index 0000000..2c2d240 --- /dev/null +++ b/frontend/src/types/Transaction.ts @@ -0,0 +1,4 @@ +export type TxStatus = { + status: "pending" | "success"; + hash: string; +}; diff --git a/frontend/src/utils/constants.js b/frontend/src/utils/constants.ts similarity index 89% rename from frontend/src/utils/constants.js rename to frontend/src/utils/constants.ts index 857d2c1..1357351 100644 --- a/frontend/src/utils/constants.js +++ b/frontend/src/utils/constants.ts @@ -1,4 +1,11 @@ -export const supportedChains = [ +export type ChainInfo = { + chainId: number, + disperseAddress: string, + blockExplorer: string, + name: string, +}; + +export const supportedChains: ChainInfo[] = [ { chainId: 5, disperseAddress: "0xD286f3D834E6030F178C395C9ba33d32B427cAD3", diff --git a/frontend/src/utils/index.js b/frontend/src/utils/index.ts similarity index 65% rename from frontend/src/utils/index.js rename to frontend/src/utils/index.ts index a48b71e..08adf72 100644 --- a/frontend/src/utils/index.js +++ b/frontend/src/utils/index.ts @@ -1,9 +1,11 @@ import { ethers } from "ethers"; -import { supportedChains } from "./constants"; +import { ChainInfo, supportedChains } from "./constants"; +import { RecipientInfo } from "../types/Recipient"; -export const isValidAddress = (address) => ethers.utils.isAddress(address); +export const isValidAddress = (address: string) => + ethers.utils.isAddress(address); -export const isValidValue = (value) => { +export const isValidValue = (value: string): ethers.BigNumber | false => { try { return ethers.utils.parseUnits(value, "ether"); } catch (err) { @@ -11,13 +13,13 @@ export const isValidValue = (value) => { } }; -export const isChainSupported = (chainId) => +export const isChainSupported = (chainId: number) => supportedChains.some((chain) => chain.chainId === chainId); -export const getNetworkInfo = (chainId) => +export const getNetworkInfo = (chainId: number): ChainInfo | undefined => supportedChains.find((chain) => chain.chainId === chainId); -export const getWarnMessage = () => { +export const getWarnMessage = (): string => { let networks = ``; supportedChains.map((chain, i) => { if (i === 0) { @@ -31,9 +33,9 @@ export const getWarnMessage = () => { return `*Supports ${networks}*`; }; -export const parseText = (textValue) => { +export const parseText = (textValue: string): RecipientInfo[] => { const lines = textValue.split("\n"); - let updatedRecipients = []; + let updatedRecipients: RecipientInfo[] = []; lines.map((line) => { if ( diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..85fd126 --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1,5 @@ +/// + +interface Window { + ethereum: any; +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..a7fc6fb --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000..42872c5 --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.js b/frontend/vite.config.ts similarity index 100% rename from frontend/vite.config.js rename to frontend/vite.config.ts