diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index bffb357..369a678 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -1,3 +1,3 @@ { - "extends": "next/core-web-vitals" + "extends": ["plugin:react/recommended", "next/core-web-vitals"] } diff --git a/frontend/components/context/Web3Context.tsx b/frontend/components/context/Web3Context.tsx index 84c6bb1..218225d 100644 --- a/frontend/components/context/Web3Context.tsx +++ b/frontend/components/context/Web3Context.tsx @@ -1,32 +1,12 @@ import { createContext, useState, useEffect } from 'react'; -import type { BigNumberish } from '@ethersproject/bignumber'; import type { Web3 } from '@components/navbar/MetaMask'; -import { formatEther } from '@ethersproject/units'; declare let window: any; -export const Web3Context = createContext(null); +export const Web3Context = createContext(null!); const Web3ProviderComponent: React.FC = ({ children }) => { - const [newRegister, setNewRegister] = useState(); - const [ - { - gameContract, - nftContract, - provider, - account, - etherBalance, - tokenBalance - }, - setWeb3 - ] = useState({ - gameContract: '', - nftContract: '', - provider: '', - account: '', - etherBalance: '', - tokenBalance: '' - }); + const [{ contract, provider, account }, setWeb3] = useState({} as Web3); // Listens for network changes to reload the page useEffect(() => { @@ -41,43 +21,27 @@ const Web3ProviderComponent: React.FC = ({ children }) => { // Listens for a change in account and updates state useEffect(() => { - if (gameContract && provider) { - function newAccount(accounts: Array) { - const signer = provider?.getSigner(accounts[0]); - - gameContract - .balanceOf(signer._address) - .then((tokenBalance: BigNumberish) => { - signer?.getBalance().then((balance: BigNumberish) => - setWeb3((prev: Web3) => ({ - ...prev, - gameContract: gameContract?.connect(signer), - nftContract: nftContract?.connect(signer), - account: signer._address, - etherBalance: Number(formatEther(balance)).toFixed(4), - tokenBalance: Number(formatEther(tokenBalance)).toFixed(4) - })) - ); - }); - } - - window.ethereum.on('accountsChanged', newAccount); - return () => - window.ethereum.removeListener('accountsChanged', newAccount); + function newAccount(accounts: Array) { + const signer = provider?.getSigner(accounts[0]); + + setWeb3((prev: Web3) => ({ + ...prev, + contract: Contract__factory.connect(signer), + account: signer._address + })); } + + window.ethereum.on('accountsChanged', newAccount); + return () => window.ethereum.removeListener('accountsChanged', newAccount); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [account]); return ( diff --git a/frontend/components/helpers/handleToast.ts b/frontend/components/helpers/handleToast.ts new file mode 100644 index 0000000..a4de2ec --- /dev/null +++ b/frontend/components/helpers/handleToast.ts @@ -0,0 +1,29 @@ +import { + toast, + ToastOptions, + Theme, + ToastPosition, + TypeOptions +} from 'react-toastify'; + +function handleToast( + type: TypeOptions, + message: string, + position: ToastPosition = 'top-center', + theme: Theme = 'colored' +) { + const options: ToastOptions = { + type, + position, + theme, + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true + }; + + toast(message, options); +} + +export default handleToast; diff --git a/frontend/components/navbar/MetaMask.tsx b/frontend/components/navbar/MetaMask.tsx index 9944176..f092b7e 100644 --- a/frontend/components/navbar/MetaMask.tsx +++ b/frontend/components/navbar/MetaMask.tsx @@ -1,22 +1,16 @@ import { useContext } from 'react'; import { Web3Context } from '@components/context/Web3Context'; -import { gameABI } from '@components/ABIs/gameABI'; -import { nftABI } from '@components/ABIs/nftABI'; - import { Web3Provider } from '@ethersproject/providers'; -import { Contract } from '@ethersproject/contracts'; -import { formatEther } from '@ethersproject/units'; + import { toast } from 'react-toastify'; declare let window: any; export interface Web3 { - gameContract: any; - nftContract: Contract | string; + contract: ContractFactoryInterface; provider: Web3Provider; account: string; - etherBalance: string; - tokenBalance: string; + setWeb3?: React.Dispatch>; } const MetaMask = () => { @@ -33,63 +27,40 @@ const MetaMask = () => { }); const chainId = await ethereum.request({ method: 'eth_chainId' }); - let nftAddress; - let gameAddress; + let contractAddress: string; switch (chainId) { case '0x1': // Mainnet - nftAddress = ''; - gameAddress = ''; + contractAddress = ''; break; case '0x3': // Ropsten - nftAddress = ''; - gameAddress = ''; + contractAddress = ''; break; - case '0x4': // Rinkeby - nftAddress = '0x8F7117FeA39fdB8E3335090d7211A6128E4d5cC0'; - gameAddress = '0x141EC0C2b4C193675594EAf316faAD8784dA8A11'; + case '0x4': + contractAddress = ''; break; case '0x89': // Polygon Mainnet - nftAddress = ''; - gameAddress = ''; + contractAddress = ''; break; case '0x13881': // Polygon Testnet - nftAddress = ''; - gameAddress = ''; + contractAddress = ''; break; - case '0x7a69': // Hardhat Local - nftAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3'; - gameAddress = '0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512'; + default: + // Hardhat Local + contractAddress = '0x5FbDB2315678afecb367f032d93F642f64180aa3'; } const signer = provider.getSigner(address); - - const nftContract = nftAddress - ? new Contract(nftAddress, nftABI, signer) - : ''; - const gameContract: any = gameAddress - ? new Contract(gameAddress, gameABI, signer) - : ''; - const account = signer._address; - const tokenBalance = Number( - formatEther(await gameContract.balanceOf(account)) - ).toFixed(4); - console.log(tokenBalance); - - const etherBalance = Number( - formatEther(await signer.getBalance()) - ).toFixed(4); + const contract = Contract__factory.connect(contractAddress, signer); - setWeb3((prev: Web3) => ({ - ...prev, - gameContract, - nftContract, - provider, - account, - etherBalance, - tokenBalance - })); + setWeb3 && + setWeb3((prev: Web3) => ({ + ...prev, + contract, + provider, + account + })); } else if (window.web3) { console.log('Update MetaMask'); } else { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 23f3768..11dd564 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,6 +19,7 @@ }, "devDependencies": { "@types/dotenv": "^8.2.0", + "@types/lodash": "^4.14.175", "@types/node": "^16.9.1", "@types/react": "17.0.20", "@types/react-dom": "^17.0.9", @@ -3622,6 +3623,12 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.14.175", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==", + "dev": true + }, "node_modules/@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", @@ -18024,6 +18031,12 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/lodash": { + "version": "4.14.175", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==", + "dev": true + }, "@types/long": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 3dac40d..b37c4d3 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ }, "devDependencies": { "@types/dotenv": "^8.2.0", + "@types/lodash": "^4.14.175", "@types/node": "^16.9.1", "@types/react": "17.0.20", "@types/react-dom": "^17.0.9", diff --git a/frontend/styles/globals.css b/frontend/styles/globals.css index 363876f..9b3333d 100644 --- a/frontend/styles/globals.css +++ b/frontend/styles/globals.css @@ -18,6 +18,10 @@ button { @apply dark:shadow-2xl; } +ol { + @apply list-decimal; +} + .link { @apply underline text-blue-600 hover:text-blue-800 dark:hover:text-blue-300 dark:text-blue-400; @@ -49,7 +53,8 @@ button { .input-field { @apply shadow appearance-none border rounded py-2 px-3 - text-gray-700 leading-tight focus:outline-none; + text-gray-700 leading-tight focus:outline-none + dark:bg-gray-700 dark:text-gray-100 dark:border-gray-300 dark:shadow-2xl; } .input-label { @@ -57,8 +62,8 @@ button { } .action-card { - @apply flex flex-col p-5 border w-min - rounded-lg shadow dark:shadow-2xl; + @apply flex flex-col p-5 border w-min bg-blueGray-50 + rounded-lg shadow dark:shadow-2xl dark:bg-coolGray-800; } .game-card { @@ -68,20 +73,27 @@ button { .green-btn { @apply text-white font-bold py-1 px-4 mt-5 - rounded bg-green-bp1 hover:bg-green-bp3; + rounded bg-green-bp1 hover:bg-green-bp3 + shadow dark:shadow-2xl; } .metamask-btn { @apply text-white font-bold py-1 px-4 - rounded bg-green-bp1 hover:bg-green-bp3; + rounded bg-green-bp1 hover:bg-green-bp3 + shadow dark:shadow-2xl; } .blue-btn { @apply bg-blue-500 hover:bg-blue-600 text-white - font-bold py-1 px-4 rounded; + font-bold py-1 px-4 rounded shadow dark:shadow-2xl; } .disabled-btn { @apply bg-gray-500 text-white font-bold py-2 px-4 - rounded opacity-50 cursor-not-allowed; + rounded opacity-50 cursor-not-allowed shadow dark:shadow-2xl; +} + +.nft-card { + @apply m-10 max-w-xs rounded-2xl overflow-hidden + shadow-xl dark:shadow-2xl dark:bg-gray-700; } diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 248099d..9b34524 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "target": "es6", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": ["dom", "dom.iterable", "esnext", "es6", "es5"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -20,5 +20,5 @@ } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "exclude": ["node_modules", "components/typechain"] }