From 45cbd5f976abea14e532087cf0d186b27e91dd76 Mon Sep 17 00:00:00 2001 From: vignesha22 <82584664+vignesha22@users.noreply.github.com> Date: Mon, 4 Mar 2024 22:39:04 +0530 Subject: [PATCH] PRO-2166-Frontend Changes (#69) * added remove Whitelist API * added remove whitelist frontend --- admin_frontend/package-lock.json | 4 +- admin_frontend/package.json | 2 +- backend/package.json | 2 +- backend/src/constants/ErrorMessage.ts | 2 + backend/src/paymaster/index.ts | 30 +- backend/src/routes/index.ts | 56 + frontend/.editorconfig | 12 + frontend/.gitignore | 2 + frontend/package-lock.json | 4 +- frontend/package.json | 2 +- frontend/src/components/ConnectedIcon.jsx | 12 +- frontend/src/components/Dashboard.jsx | 1132 +++++++++-------- frontend/src/components/Header.jsx | 45 +- frontend/src/components/NotFound.jsx | 26 +- frontend/src/components/ProtectedRoute.js | 2 +- .../src/components/TransactionSentToast.jsx | 46 +- frontend/src/context/AuthContext.js | 2 +- frontend/src/utils/constant.js | 4 + 18 files changed, 763 insertions(+), 622 deletions(-) create mode 100644 frontend/.editorconfig diff --git a/admin_frontend/package-lock.json b/admin_frontend/package-lock.json index 736fa53..bd3c274 100644 --- a/admin_frontend/package-lock.json +++ b/admin_frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "admin_frontend", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "admin_frontend", - "version": "1.0.2", + "version": "1.0.3", "dependencies": { "@emotion/react": "11.11.3", "@emotion/styled": "11.11.0", diff --git a/admin_frontend/package.json b/admin_frontend/package.json index c234130..9c9f593 100644 --- a/admin_frontend/package.json +++ b/admin_frontend/package.json @@ -1,6 +1,6 @@ { "name": "admin_frontend", - "version": "1.0.2", + "version": "1.0.3", "private": true, "dependencies": { "@emotion/react": "11.11.3", diff --git a/backend/package.json b/backend/package.json index b48d40d..8ecc9d1 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "arka", - "version": "1.1.2", + "version": "1.1.3", "description": "ARKA - (Albanian for Cashier's case) is the first open source Paymaster as a service software", "type": "module", "directories": { diff --git a/backend/src/constants/ErrorMessage.ts b/backend/src/constants/ErrorMessage.ts index 06e4616..1bf8bee 100644 --- a/backend/src/constants/ErrorMessage.ts +++ b/backend/src/constants/ErrorMessage.ts @@ -7,6 +7,8 @@ export default { FAILED_TO_PROCESS: 'Failed to process the request. Please try again or contact ARKA support team', INVALID_MODE: 'Invalid mode selected', DUPLICATE_RECORD: 'Duplicate record found', + ERROR_ON_SUBMITTING_TXN: 'The wallet does not have enough funds or the gas price is too high at the moment. Please try again later or contact support team', + RPC_ERROR: 'rpcError while checking whitelist. Please try again later', QUOTA_EXCEEDED: 'Quota exceeded for this month', INVALID_USER: 'Unauthorised User', RECORD_NOT_FOUND: 'Api Key provided not found', diff --git a/backend/src/paymaster/index.ts b/backend/src/paymaster/index.ts index 4c55031..8a16632 100644 --- a/backend/src/paymaster/index.ts +++ b/backend/src/paymaster/index.ts @@ -3,6 +3,7 @@ import { providers, Wallet, ethers, Contract } from 'ethers'; import { arrayify, defaultAbiCoder, hexConcat } from 'ethers/lib/utils.js'; import abi from "../abi/EtherspotAbi.js"; import { PimlicoPaymaster, getERC20Paymaster } from './pimlico.js'; +import ErrorMessage from 'constants/ErrorMessage.js'; export class Paymaster { @@ -116,7 +117,30 @@ export class Paymaster { }; } catch (err: any) { if (err.message.includes('already whitelisted')) throw new Error(err); - throw new Error('Error while submitting transaction'); + throw new Error(ErrorMessage.ERROR_ON_SUBMITTING_TXN); + } + } + + async removeWhitelistAddress(address: string[], paymasterAddress: string, bundlerRpc: string, relayerKey: string) { + try { + const provider = new providers.JsonRpcProvider(bundlerRpc); + const paymasterContract = new ethers.Contract(paymasterAddress, abi, provider); + const signer = new Wallet(relayerKey, provider) + for (let i = 0; i < address.length; i++) { + const isAdded = await paymasterContract.check(signer.address, address[i]); + if (!isAdded) { + throw new Error(`${address[i]} is not whitelisted`) + } + } + const encodedData = paymasterContract.interface.encodeFunctionData('removeBatchToWhitelist', [address]); + const tx = await signer.sendTransaction({ to: paymasterAddress, data: encodedData }); + await tx.wait(); + return { + message: `Successfully removed whitelisted addresses with transaction Hash ${tx.hash}` + }; + } catch (err: any) { + if (err.message.includes('is not whitelisted')) throw new Error(err); + throw new Error(ErrorMessage.ERROR_ON_SUBMITTING_TXN); } } @@ -127,7 +151,7 @@ export class Paymaster { const paymasterContract = new ethers.Contract(paymasterAddress, abi, provider); return paymasterContract.check(signer.address, accountAddress); } catch (err) { - throw new Error('rpcError while checking whitelist'); + throw new Error(ErrorMessage.RPC_ERROR); } } @@ -146,7 +170,7 @@ export class Paymaster { message: `Successfully deposited with transaction Hash ${tx.hash}` }; } catch (err) { - throw new Error('Error while submitting transaction'); + throw new Error(ErrorMessage.ERROR_ON_SUBMITTING_TXN); } } } diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index 7a2fe71..dbbc589 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -320,6 +320,62 @@ const routes: FastifyPluginAsync = async (server) => { } ) + server.post("/removeWhitelist", async function (request, reply) { + try { + const body: any = request.body; + const query: any = request.query; + const address = body.params[0]; + const chainId = query['chainId'] ?? body.params[1]; + const api_key = query['apiKey'] ?? body.params[2]; + if (!api_key) + return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) + let privateKey = ''; + let supportedNetworks; + if (!unsafeMode) { + const AWSresponse = await client.send( + new GetSecretValueCommand({ + SecretId: prefixSecretId + api_key, + }) + ); + const secrets = JSON.parse(AWSresponse.SecretString ?? '{}'); + if (!secrets['PRIVATE_KEY']) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) + privateKey = secrets['PRIVATE_KEY']; + supportedNetworks = secrets['SUPPORTED_NETWORKS']; + } else { + const record: any = await getSQLdata(api_key); + console.log(record); + if (!record) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) + privateKey = decode(record['PRIVATE_KEY']); + supportedNetworks = record['SUPPORTED_NETWORKS']; + } + if (!privateKey) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }) + if ( + !Array.isArray(address) || + address.length > 10 || + !chainId || + isNaN(chainId) + ) { + return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA }); + } + if (server.config.SUPPORTED_NETWORKS == '' && !SupportedNetworks) { + return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); + } + const networkConfig = getNetworkConfig(chainId, supportedNetworks ?? ''); + if (!networkConfig) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.UNSUPPORTED_NETWORK }); + const validAddresses = address.every(ethers.utils.isAddress); + if (!validAddresses) return reply.code(ReturnCode.FAILURE).send({ error: "Invalid Address passed" }); + const result = await paymaster.removeWhitelistAddress(address, networkConfig.contracts.etherspotPaymasterAddress, networkConfig.bundler, privateKey); + if (body.jsonrpc) + return reply.code(ReturnCode.SUCCESS).send({ jsonrpc: body.jsonrpc, id: body.id, result, error: null }) + return reply.code(ReturnCode.SUCCESS).send(result); + } catch (err: any) { + request.log.error(err); + if (err.name == "ResourceNotFoundException") + return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_API_KEY }); + return reply.code(ReturnCode.FAILURE).send({ error: err.message ?? ErrorMessage.SOMETHING_WENT_WRONG }) + } + }) + server.post( "/checkWhitelist", async function (request, reply) { diff --git a/frontend/.editorconfig b/frontend/.editorconfig new file mode 100644 index 0000000..64d3d6a --- /dev/null +++ b/frontend/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore index f2ad830..2c69316 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -14,3 +14,5 @@ chrome-user-data .vscode *.swp *.swo + +.env.local diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d816516..d7bc01d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "arka_frontend", - "version": "1.0.2", + "version": "1.0.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "arka_frontend", - "version": "1.0.2", + "version": "1.0.3", "dependencies": { "@babel/plugin-proposal-private-property-in-object": "7.21.11", "@emotion/react": "^11.11.1", diff --git a/frontend/package.json b/frontend/package.json index a0e374d..065d6bf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "arka_frontend", - "version": "1.0.2", + "version": "1.0.3", "private": true, "dependencies": { "@babel/plugin-proposal-private-property-in-object": "7.21.11", diff --git a/frontend/src/components/ConnectedIcon.jsx b/frontend/src/components/ConnectedIcon.jsx index 4084f53..c83d494 100644 --- a/frontend/src/components/ConnectedIcon.jsx +++ b/frontend/src/components/ConnectedIcon.jsx @@ -1,13 +1,13 @@ import React, { Fragment } from "react"; const ColoredCircle = ({ color }) => { - const styles = { backgroundColor: color }; + const styles = { backgroundColor: color }; - return color ? ( - - - - ) : null; + return color ? ( + + + + ) : null; }; export default ColoredCircle; diff --git a/frontend/src/components/Dashboard.jsx b/frontend/src/components/Dashboard.jsx index 3bdeb39..3d1ea19 100644 --- a/frontend/src/components/Dashboard.jsx +++ b/frontend/src/components/Dashboard.jsx @@ -28,54 +28,54 @@ import { UserAuth } from "../context/AuthContext"; import EtherspotLogo from "../assets/internal-36-etherspot@2x.png"; // constants -import { networks } from "../utils/constant"; +import { networks, ENDPOINTS } from "../utils/constant"; import EtherspotPaymasterAbi from "../abi/EtherspotPaymasterAbi.json"; const ITEM_HEIGHT = 48; const MenuProps = { - PaperProps: { - style: { - maxHeight: ITEM_HEIGHT * 4.5, - width: 250, - backgroundColor: "#1c1c1c", - color: "white", - border: "none", - }, - }, + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5, + width: 250, + backgroundColor: "#1c1c1c", + color: "white", + border: "none", + }, + }, }; function CustomTabPanel(props) { - const { children, value, index, ...other } = props; + const { children, value, index, ...other } = props; - return ( - - ); + return ( + + ); } CustomTabPanel.propTypes = { - children: PropTypes.node, - index: PropTypes.number.isRequired, - value: PropTypes.number.isRequired, + children: PropTypes.node, + index: PropTypes.number.isRequired, + value: PropTypes.number.isRequired, }; function a11yProps(index) { - return { - key: `tab-${index}`, - "aria-controls": `tabpanel-${index}`, - }; + return { + key: `tab-${index}`, + "aria-controls": `tabpanel-${index}`, + }; } const DashBoardPage = styled.div` @@ -86,546 +86,580 @@ const DashBoardPage = styled.div` `; const Dashboard = ({ logInType }) => { - // Definitions - const { user, signIn } = UserAuth(); - const [value, setValue] = React.useState(0); - const [chainId, setChainId] = useState(80001); - const [signedIn, setSignedIn] = useState(false); - const [networksSupported, setNetworksSupported] = useState(Object.keys(networks)); - const [supportedNetworks, setSupportedNetworks] = useState(); - const [isLoading, setIsLoading] = useState(false); - const [paymasterBalance, setPaymasterBalance] = useState("0"); - const [useCustomPaymaster] = useState(false); - const [customPaymasterAddress] = useState(); - const [selectedOption, setSelectedOption] = useState(0); - const [amount, setAmount] = useState(0); - const [checked, setChecked] = useState(false); - const [buttonText, setButtonText] = useState("Deposit"); - const [whiteListAddress, setWhitelistAddress] = useState(""); + // Definitions + const { user, signIn } = UserAuth(); + const [value, setValue] = React.useState(0); + const [chainId, setChainId] = useState(80001); + const [signedIn, setSignedIn] = useState(false); + const [networksSupported, setNetworksSupported] = useState( + Object.keys(networks) + ); + const [supportedNetworks, setSupportedNetworks] = useState(); + const [isLoading, setIsLoading] = useState(false); + const [paymasterBalance, setPaymasterBalance] = useState("0"); + const [useCustomPaymaster] = useState(false); + const [customPaymasterAddress] = useState(); + const [selectedOption, setSelectedOption] = useState(0); + const [amount, setAmount] = useState(0); + const [checked, setChecked] = useState(false); + const [buttonText, setButtonText] = useState("Deposit"); + const [whiteListAddress, setWhitelistAddress] = useState(""); + const [removeWhitelist, setRemoveWhitelist] = useState(false); + const [whitelistButtonText, setWhitelistButtonText] = + useState("Add to Whitelist"); - // Functions - const getPaymasterContract = (chainId) => { - if (!supportedNetworks) return null; - const provider = new ethers.providers.JsonRpcProvider( - supportedNetworks[chainId].rpcUrl, - { - name: "Connected Bundler", - chainId: Number(chainId), - } - ); - if (useCustomPaymaster) { - return new ethers.Contract( - customPaymasterAddress, - EtherspotPaymasterAbi, - provider - ); - } else { - return new ethers.Contract( - supportedNetworks[chainId].paymasterAddress, - EtherspotPaymasterAbi, - provider - ); - } - }; + // Functions + const getPaymasterContract = (chainId) => { + if (!supportedNetworks) return null; + const provider = new ethers.providers.JsonRpcProvider( + supportedNetworks[chainId].rpcUrl, + { + name: "Connected Bundler", + chainId: Number(chainId), + } + ); + if (useCustomPaymaster) { + return new ethers.Contract( + customPaymasterAddress, + EtherspotPaymasterAbi, + provider + ); + } else { + return new ethers.Contract( + supportedNetworks[chainId].paymasterAddress, + EtherspotPaymasterAbi, + provider + ); + } + }; - const fetchData = async (address) => { + const fetchData = async (address) => { try { setIsLoading(true); const data = await ( - await fetch("http://localhost:5050/getSupportedNetworks", { + await fetch(`${process.env.REACT_APP_SERVER_URL}${ENDPOINTS['getSupportedNetworks']}`, { method: "POST", - body: JSON.stringify({WALLET_ADDRESS: address}) + body: JSON.stringify({ WALLET_ADDRESS: address }), }) ).json(); - const supportedNetworksChainIds = []; - let supportedNetworks = {} + const supportedNetworksChainIds = []; + let supportedNetworks = {}; - data?.map((value) => { - supportedNetworks[value.chainId] = { - ...networks[value.chainId], - chainId: value.chainId, - rpcUrl: value.bundler, - paymasterAddress: value.contracts.etherspotPaymasterAddress, - } - supportedNetworksChainIds.push(value.chainId) - return value; - }); - setSupportedNetworks(supportedNetworks); - setNetworksSupported(supportedNetworksChainIds); + data?.map((value) => { + supportedNetworks[value.chainId] = { + ...networks[value.chainId], + chainId: value.chainId, + rpcUrl: value.bundler, + paymasterAddress: value.contracts.etherspotPaymasterAddress, + }; + supportedNetworksChainIds.push(value.chainId); + return value; + }); + setSupportedNetworks(supportedNetworks); + setNetworksSupported(supportedNetworksChainIds); setIsLoading(false); } catch (err) { - toast.error( - "Check Backend Service for more info" - ); + toast.error("Check Backend Service for more info"); + } + }; + + const getPaymasterBalance = async (chainId) => { + try { + if (!isLoading) { + setIsLoading(true); + const PaymasterContract = getPaymasterContract(chainId); + if (PaymasterContract) { + const balance = await PaymasterContract.getSponsorBalance( + user?.address + ); + setPaymasterBalance(ethers.utils.formatEther(balance)); + } + setIsLoading(false); + } + } catch (err) { + console.error(err); + setPaymasterBalance("0"); } }; - const getPaymasterBalance = async (chainId) => { - try { - if (!isLoading) { - setIsLoading(true); - const PaymasterContract = getPaymasterContract(chainId); - if (PaymasterContract) { - const balance = await PaymasterContract.getSponsorBalance( - user?.address - ); - setPaymasterBalance(ethers.utils.formatEther(balance)); - } - setIsLoading(false); - } - } catch (err) { - console.error(err); - setPaymasterBalance("0"); - } - }; + useEffect(() => { + setIsLoading(false); + if (user?.address) { + setSignedIn(true); + fetchData(user.address); + } + setIsLoading(false); + }, [user]); - useEffect(() => { - setIsLoading(false); - if (user?.address) { - setSignedIn(true); - fetchData(user.address); - } - setIsLoading(false); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [user, isLoading]); + useEffect(() => { + getPaymasterBalance(chainId); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [supportedNetworks]); - useEffect(() => { - getPaymasterBalance(chainId) - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [supportedNetworks]); + const handleChange = (event, newValue) => { + setValue(newValue); + }; - const handleChange = (event, newValue) => { - setValue(newValue); - }; + const handleAmountChange = (e) => { + const fixed = parseFloat(e.target.value).toFixed(18).toString(); + if (fixed.length < parseFloat(e.target.value).toString().length) + e.target.value = fixed; + setAmount(e.target.value); + }; - const handleAmountChange = (e) => { - const fixed = parseFloat(e.target.value).toFixed(18).toString(); - if (fixed.length < parseFloat(e.target.value).toString().length) - e.target.value = fixed; - setAmount(e.target.value); - }; + const handleDepositChange = (e) => { + setChecked(e.target.checked); + if (e.target.checked) setButtonText("Withdraw"); + else setButtonText("Deposit"); + }; - const handleDepositChange = (e) => { - setChecked(e.target.checked); - if (e.target.checked) setButtonText("Withdraw"); - else setButtonText("Deposit"); - }; + const handleWhitelistSwitch = (e) => { + setRemoveWhitelist(e.target.checked); + if (e.target.checked) setWhitelistButtonText("Remove from Whitelist"); + else setWhitelistButtonText("Add to Whitelist"); + }; - const handleWhitelistAddressChange = (e) => { - setWhitelistAddress(e.target.value); - }; + const handleWhitelistAddressChange = (e) => { + setWhitelistAddress(e.target.value); + }; - const handleChangeChainId = async (event, newValue) => { - setIsLoading(true); - setPaymasterBalance("0"); - setChainId(newValue); - await getPaymasterBalance(newValue); - setIsLoading(false); - }; + const handleChangeChainId = async (event, newValue) => { + setIsLoading(true); + setPaymasterBalance("0"); + setChainId(newValue); + await getPaymasterBalance(newValue); + setIsLoading(false); + }; - const handleOptionChange = (e) => { - setSelectedOption(e.target.value); - }; + const handleOptionChange = (e) => { + setSelectedOption(e.target.value); + }; - const handleSubmit = async () => { - try { - setIsLoading(true); - if (!user?.address) { - const retUser = await signIn(logInType); - if (!retUser) { - toast.error("Please make sure that metamask is installed"); - } else toast.success("Logged in Successfully"); - return; - } - if (amount === 0) { - toast.error("Please enter an amount to be Deposited/Withdrawn"); - } - const PaymasterContract = getPaymasterContract(chainId); - if (checked) { - const encodedData = PaymasterContract.interface.encodeFunctionData( - "withdrawFunds", - [ethers.utils.parseEther(amount.toString())] - ); - const txHash = await window.ethereum.request({ - method: "eth_sendTransaction", - params: [ - { - from: user.address, // The user's active address. - to: PaymasterContract.address, // Required except during contract publications. - data: encodedData, - }, - ], - }); - toast.loading( - (t) => ( - - ), - { - icon: "👏", - } - ); - } else { - const encodedData = PaymasterContract.interface.encodeFunctionData( - "depositFunds", - [] - ); - const txHash = await window.ethereum.request({ - method: "eth_sendTransaction", - params: [ - { - from: user.address, // The user's active address. - to: PaymasterContract.address, // Required except during contract publications. - data: encodedData, - value: ethers.utils.parseEther(amount.toString()).toHexString(), - }, - ], - }); - toast.loading( - (t) => ( - - ), - { - icon: "👏", - } - ); - } - setIsLoading(false); - } catch (e) { - console.error(e.message); - toast.error("Something went wrong on MetaMask"); - setIsLoading(false); - } - }; + const handleSubmit = async () => { + try { + setIsLoading(true); + if (!user?.address) { + const retUser = await signIn(logInType); + if (!retUser) { + toast.error("Please make sure that metamask is installed"); + } else toast.success("Logged in Successfully"); + return; + } + if (amount === 0) { + toast.error("Please enter an amount to be Deposited/Withdrawn"); + } + const PaymasterContract = getPaymasterContract(chainId); + if (checked) { + const encodedData = PaymasterContract.interface.encodeFunctionData( + "withdrawFunds", + [ethers.utils.parseEther(amount.toString())] + ); + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [ + { + from: user.address, // The user's active address. + to: PaymasterContract.address, // Required except during contract publications. + data: encodedData, + }, + ], + }); + toast.loading( + (t) => ( + + ), + { + icon: "👏", + } + ); + } else { + const encodedData = PaymasterContract.interface.encodeFunctionData( + "depositFunds", + [] + ); + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [ + { + from: user.address, // The user's active address. + to: PaymasterContract.address, // Required except during contract publications. + data: encodedData, + value: ethers.utils.parseEther(amount.toString()).toHexString(), + }, + ], + }); + toast.loading( + (t) => ( + + ), + { + icon: "👏", + } + ); + } + setIsLoading(false); + } catch (e) { + console.error(e.message); + toast.error("Something went wrong while submitting. Please check your injected wallet"); + setIsLoading(false); + } + }; - const handleWhitelistSubmit = async () => { - try { - setIsLoading(true); - if (!ethers.utils.isAddress(whiteListAddress)) { - toast.error("Invalid Address provided"); - } else { - const PaymasterContract = getPaymasterContract(chainId); - const checkIfAdded = await PaymasterContract.check( - user.address, - whiteListAddress - ); - if (checkIfAdded) { - toast.error("The Address has been already added to the whitelist"); - } else { - const encodedData = PaymasterContract.interface.encodeFunctionData( - "addToWhitelist", - [whiteListAddress] - ); - const txHash = await window.ethereum.request({ - method: "eth_sendTransaction", - params: [ - { - from: user.address, // The user's active address. - to: PaymasterContract.address, // Required except during contract publications. - data: encodedData, - }, - ], - }); - toast.loading( - (t) => ( - - ), - { - icon: "👏", - } - ); - } - } - setIsLoading(false); - } catch (err) { - console.error(err); - toast.error("Something went wrong on MetaMask"); - setIsLoading(false); - } - }; + const handleWhitelistSubmit = async () => { + try { + setIsLoading(true); + if (!ethers.utils.isAddress(whiteListAddress)) { + toast.error("Invalid Address provided"); + } else { + const PaymasterContract = getPaymasterContract(chainId); + const checkIfAdded = await PaymasterContract.check( + user.address, + whiteListAddress + ); + if (checkIfAdded && !removeWhitelist) { + toast.error("The Address has been already added to the whitelist"); + setIsLoading(false); + return; + } + if (!checkIfAdded && removeWhitelist) { + toast.error("The Address has not been added to the whitelist"); + setIsLoading(false); + return; + } + const encodedData = PaymasterContract.interface.encodeFunctionData( + removeWhitelist ? "removeFromWhitelist" : "addToWhitelist", + [whiteListAddress] + ); + const txHash = await window.ethereum.request({ + method: "eth_sendTransaction", + params: [ + { + from: user.address, // The user's active address. + to: PaymasterContract.address, // Required except during contract publications. + data: encodedData, + }, + ], + }); + toast.loading( + (t) => ( + + ), + { + icon: "👏", + } + ); + } + setIsLoading(false); + } catch (err) { + console.error(err); + toast.error("Something went wrong while submitting. Please check your injected wallet"); + setIsLoading(false); + } + }; - return ( - <> - -
-
- - - - - - - {networksSupported?.length && supportedNetworks ? ( -
- - {networksSupported.map((network, index) => { - return ( - } - iconPosition="start" - label={networks[network].label} - value={network} - disabled={!signedIn} - {...a11yProps(networks[network]?.label)} - tabIndex={index} - /> - ); - })} - -
- ) : ( - <> - )} - {paymasterBalance !== "0" ? ( -
- Balance: {Number(paymasterBalance).toFixed(5)}{" "} - getPaymasterBalance(chainId)} - > - - -
- ) : ( - <> - )} - -
-
-
-
- - Select Paymaster - - -
-
- - Enter Amount - - - - - ), - }} - sx={[ - { - ".MuiInputBase-input": { - color: "white", - }, - ".MuiFormLabel-root": { - color: "#5c5c5c", - fontSize: "1.3rem", - }, - }, - ]} - /> -
-
-
- - Deposit/Withdraw - -
-
- -
-
-
-
- -
-
-
-
- {Number(paymasterBalance) > 0 ? ( - -
-
- Whitelist Address -
- - -
-
- ) : ( - <> - )} -
- - - ); + return ( + <> + +
+
+ + + + + + + {networksSupported?.length && supportedNetworks ? ( +
+ + {networksSupported.map((network, index) => { + return ( + + } + iconPosition="start" + label={networks[network].label} + value={network} + disabled={!signedIn} + {...a11yProps(networks[network]?.label)} + tabIndex={index} + /> + ); + })} + +
+ ) : ( + <> + )} + {paymasterBalance !== "0" ? ( +
+ Balance: {Number(paymasterBalance).toFixed(5)}{" "} + getPaymasterBalance(chainId)} + > + + +
+ ) : ( + <> + )} + +
+
+
+
+ + Select Paymaster + + +
+
+ + Enter Amount + + + + + ), + }} + sx={[ + { + ".MuiInputBase-input": { + color: "white", + }, + ".MuiFormLabel-root": { + color: "#5c5c5c", + fontSize: "1.3rem", + }, + }, + ]} + /> +
+
+
+ + Deposit/Withdraw + +
+
+ +
+
+
+
+ +
+
+
+
+ {Number(paymasterBalance) > 0 ? ( + +
+
+
+ Whitelist Address +
+
+ Add + + Remove +
+
+ + +
+
+ ) : ( + <> + )} +
+ + + ); }; export default Dashboard; diff --git a/frontend/src/components/Header.jsx b/frontend/src/components/Header.jsx index 08b9559..2d8df95 100644 --- a/frontend/src/components/Header.jsx +++ b/frontend/src/components/Header.jsx @@ -6,35 +6,42 @@ import { styled } from "styled-components"; import { UserAuth } from "../context/AuthContext"; // assets -import EtherspotLogo from '../assets/internal-48-etherspot@2x.png'; +import EtherspotLogo from "../assets/internal-48-etherspot@2x.png"; const LogoText = styled.span` margin: '3px 0 4px 8px', font-size: '24px', text-align: 'center', color: '#cfcfcf' - ` + `; const Header = () => { - const { user, signIn } = UserAuth(); - const [signedIn, setSignedIn] = useState(false); + const { user, signIn } = UserAuth(); + const [signedIn, setSignedIn] = useState(false); - useEffect(() => { - if (user?.address) setSignedIn(true); - }, [user]) + useEffect(() => { + if (user?.address) setSignedIn(true); + }, [user]); - return ( -
-
- {'EtherspotLogo'} - Etherspot Arka -
- { signedIn ? {user?.address} : - } -
- ); + return ( +
+
+ {"EtherspotLogo"} + Etherspot Arka +
+ {signedIn ? ( + {user?.address} + ) : ( + + )} +
+ ); }; export default Header; diff --git a/frontend/src/components/NotFound.jsx b/frontend/src/components/NotFound.jsx index 647cad0..af9363d 100644 --- a/frontend/src/components/NotFound.jsx +++ b/frontend/src/components/NotFound.jsx @@ -4,21 +4,21 @@ import { useNavigate } from "react-router-dom"; import { UserAuth } from "../context/AuthContext"; const NotFound = () => { - const { user } = UserAuth(); - const navigate = useNavigate(); + const { user } = UserAuth(); + const navigate = useNavigate(); - useEffect(() => { - if(!user) { - toast.error('no such user exists'); - navigate('/'); - } - }, [user, navigate]); + useEffect(() => { + if (!user) { + toast.error("no such user exists"); + navigate("/"); + } + }, [user, navigate]); - return ( -
-

Page Not Found

-
- ); + return ( +
+

Page Not Found

+
+ ); }; export default NotFound; diff --git a/frontend/src/components/ProtectedRoute.js b/frontend/src/components/ProtectedRoute.js index 4eb97d7..8b7482d 100644 --- a/frontend/src/components/ProtectedRoute.js +++ b/frontend/src/components/ProtectedRoute.js @@ -1,4 +1,4 @@ -import React, {useEffect} from 'react'; +import React, { useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { UserAuth } from '../context/AuthContext'; diff --git a/frontend/src/components/TransactionSentToast.jsx b/frontend/src/components/TransactionSentToast.jsx index 6ed65ab..f8f6920 100644 --- a/frontend/src/components/TransactionSentToast.jsx +++ b/frontend/src/components/TransactionSentToast.jsx @@ -1,30 +1,30 @@ import toast from "react-hot-toast"; const TransactionSentToast = ({ t, blockExplorerLink, txHash }) => { - const openInExplorer = (txHash) => { - window.open(`${blockExplorerLink}${txHash}`, "_blank"); - }; + const openInExplorer = (txHash) => { + window.open(`${blockExplorerLink}${txHash}`, "_blank"); + }; - return ( -
- Transaction Sent - - -
- ); + return ( +
+ Transaction Sent + + +
+ ); }; export default TransactionSentToast; diff --git a/frontend/src/context/AuthContext.js b/frontend/src/context/AuthContext.js index 309e14c..4055dad 100644 --- a/frontend/src/context/AuthContext.js +++ b/frontend/src/context/AuthContext.js @@ -16,7 +16,7 @@ export const AuthContextProvider = ({ children }) => { params: [address, "latest"] }) return balance; - } catch(err) { + } catch (err) { console.error('Error on retrieving balance', err); return 0; } diff --git a/frontend/src/utils/constant.js b/frontend/src/utils/constant.js index c726b43..dbc7693 100644 --- a/frontend/src/utils/constant.js +++ b/frontend/src/utils/constant.js @@ -135,3 +135,7 @@ export const networks = { blockExplorerLink: 'https://snowtrace.io/tx/' } } + +export const ENDPOINTS = { + 'getSupportedNetworks': '/getSupportedNetworks' +}