diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..2a3a13a --- /dev/null +++ b/.env.example @@ -0,0 +1,3 @@ +VITE_PAYMASTER_KEY= +VITE_BUNDLER_URL=https://bundler.biconomy.io/api/v2/80002/cJPK7B3ru.dd7f7861-190d-45ic-af80-6877f74b8f44 +VITE_SILENCE_LABS_NPM_TOKEN=npm_XXX \ No newline at end of file diff --git a/.eslintrc.cjs b/.eslintrc.cjs index d6c9537..bd9b12d 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -2,17 +2,17 @@ module.exports = { root: true, env: { browser: true, es2020: true }, extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended" ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], rules: { - 'react-refresh/only-export-components': [ - 'warn', - { allowConstantExport: true }, - ], - }, + "react-refresh/only-export-components": [ + "warn", + { allowConstantExport: true } + ] + } } diff --git a/.github/workflows/dev.yaml b/.github/workflows/dev.yaml index df4c990..0196d6f 100644 --- a/.github/workflows/dev.yaml +++ b/.github/workflows/dev.yaml @@ -31,6 +31,7 @@ jobs: CI: false NODE_OPTIONS: --max_old_space_size=4096 REACT_APP_PKEY: ${{ secrets.REACT_APP_PKEY }} + SILENCE_LABS_NPM_TOKEN: ${{ secrets.SILENCE_LABS_NPM_TOKEN }} - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 diff --git a/.github/workflows/staging.yaml b/.github/workflows/staging.yaml index 4f791e2..b6f4779 100644 --- a/.github/workflows/staging.yaml +++ b/.github/workflows/staging.yaml @@ -31,6 +31,7 @@ jobs: CI: false NODE_OPTIONS: --max_old_space_size=4096 REACT_APP_PKEY: ${{ secrets.REACT_APP_PKEY }} + SILENCE_LABS_NPM_TOKEN: ${{ secrets.SILENCE_LABS_NPM_TOKEN }} - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 diff --git a/.gitignore b/.gitignore index bd98e77..f22a89e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ dist-ssr *.njsproj *.sln *.sw? + +.env diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..9647fcd --- /dev/null +++ b/biome.json @@ -0,0 +1,63 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", + "files": { + "ignore": [ + "package.json", + "node_modules", + "**/node_modules", + "cache", + "coverage", + "tsconfig.json", + "tsconfig.*.json", + "_cjs", + "_esm", + "_types", + "bun.lockb", + "docs", + "dist", + "build" + ] + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off", + "noArrayIndexKey": "off" + }, + "style": { + "noUnusedTemplateLiteral": "off", + "noNonNullAssertion": "off", + "noUselessElse": "off", + "noParameterAssign": "off", + "useTemplate": "off" + }, + "a11y": { + "noAccessKey": "off", + "useKeyWithClickEvents": "off", + "useKeyWithMouseEvents": "off", + "useButtonType": "off" + }, + "complexity": { + "noExtraBooleanCast": "off" + } + } + }, + "formatter": { + "enabled": true, + "formatWithErrors": true, + "lineWidth": 80, + "indentWidth": 2, + "indentStyle": "space" + }, + "javascript": { + "formatter": { + "semicolons": "asNeeded", + "trailingComma": "none" + } + } +} diff --git a/bun.lockb b/bun.lockb index a51dd95..cabad5c 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000..3945b3e --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[install.scopes] +silencelaboratories = { token = "$VITE_SILENCE_LABS_NPM_TOKEN", url = "https://registry.npmjs.org" } \ No newline at end of file diff --git a/package.json b/package.json index c2699d0..08c4780 100644 --- a/package.json +++ b/package.json @@ -4,15 +4,17 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "bun i && bun link @biconomy/account && vite", + "dev": "bun i && bun link @biconomy/account @biconomy/use-aa && vite", "start": "vite", "build": "tsc && vite build", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "format": "biome format . --write", + "lint": "biome check .", + "lint:fix": "bun run lint --apply", "preview": "vite preview" }, "dependencies": { - "@biconomy/account": "^4.4.5", - "@biconomy/use-aa": "1.0.11", + "@biconomy/account": "^4.5.4", + "@biconomy/use-aa": "^1.1.0", "@emotion/react": "^11.10.6", "@emotion/styled": "^11.10.6", "@ethersproject/providers": "^5.7.2", @@ -21,16 +23,17 @@ "@mui/styles": "^5.11.12", "@rainbow-me/rainbowkit": "^2.0.0", "@tanstack/react-query": "^5.22.2", + "@types/react-toastify": "^4.1.0", "ethers": "^6.12.1", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "5.0.1", - "react-toastify": "^9.0.8", + "react-toastify": "^10.0.5", "viem": "^2.13.7", "wagmi": "^2.9.9" }, "devDependencies": { - "@types/react": "^18.3.1", + "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@typescript-eslint/eslint-plugin": "^7.2.0", "@typescript-eslint/parser": "^7.2.0", @@ -39,6 +42,11 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.6", "typescript": "^5.2.2", - "vite": "^5.2.0" + "vite": "^5.2.0", + "simple-git-hooks": "^2.9.0", + "@biomejs/biome": "1.6.0" + }, + "simple-git-hooks": { + "pre-commit": "bun run format && bun run lint:fix" } } diff --git a/src/App.tsx b/src/App.tsx index 9e3da80..f7827f1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,13 +1,13 @@ -import React from "react"; -import { makeStyles } from "@mui/styles"; -import { ToastContainer } from "react-toastify"; -import { useAccount } from "wagmi"; -import { ConnectButton } from "@rainbow-me/rainbowkit"; -import TabsBody from "./components/TabsBody"; +import { makeStyles } from "@mui/styles" +import { ConnectButton } from "@rainbow-me/rainbowkit" +import type React from "react" +import { ToastContainer } from "react-toastify" +import { useAccount } from "wagmi" +import TabsBody from "./components/TabsBody" const App: React.FC = () => { - const classes = useStyles(); - const { address } = useAccount(); + const classes = useStyles() + const { address } = useAccount() if (!address) { return ( @@ -17,7 +17,7 @@ const App: React.FC = () => { { - ); + ) } return ( @@ -39,8 +39,8 @@ const App: React.FC = () => { - ); -}; + ) +} const useStyles = makeStyles(() => ({ bgCover: { @@ -51,7 +51,7 @@ const useStyles = makeStyles(() => ({ color: "#e6e6e6", justifyContent: "start", alignItems: "center", - padding: "0px 20px", + padding: "0px 20px" }, container: { width: "60vw", @@ -64,17 +64,17 @@ const useStyles = makeStyles(() => ({ flexDirection: "column", alignItems: "start", justifyContent: "center", - gap: 20, + gap: 20 }, title: { margin: 0, fontSize: 50, - color: "#ffb999", + color: "#ffb999" }, subTitle: { fontSize: 22, - margin: 0, - }, -})); + margin: 0 + } +})) -export default App; +export default App diff --git a/src/components/AA/MintNft.tsx b/src/components/AA/MintNft.tsx index f3099ce..0bd2369 100644 --- a/src/components/AA/MintNft.tsx +++ b/src/components/AA/MintNft.tsx @@ -1,63 +1,61 @@ -import React, { useCallback, useEffect, useState } from "react"; -import { makeStyles } from "@mui/styles"; -import { usePublicClient } from "wagmi"; -import { Hex, encodeFunctionData, getContract } from "viem"; -import Button from "../Button"; -import { configInfo as config, showSuccessMessage } from "../../utils"; import { useSendTransaction, useSmartAccount, - useUserOpWait, -} from "@biconomy/use-aa"; -import { ErrorGuard } from "../../utils/ErrorGuard"; -import { polygonAmoy } from "viem/chains"; + useUserOpWait +} from "@biconomy/use-aa" +import { makeStyles } from "@mui/styles" +import type React from "react" +import { useCallback, useEffect, useState } from "react" +import { type Hex, encodeFunctionData, getContract } from "viem" +import { polygonAmoy } from "viem/chains" +import { usePublicClient } from "wagmi" +import { configInfo as config, showSuccessMessage } from "../../utils" +import { ErrorGuard } from "../../utils/ErrorGuard" +import Button from "../Button" const MintNft: React.FC = () => { - const classes = useStyles(); - const publicClient = usePublicClient(); - const [nftCount, setNftCount] = useState(null); - const [loadedCount, setLoadedCount] = useState(false); + const classes = useStyles() + const publicClient = usePublicClient() + const [nftCount, setNftCount] = useState(null) + const [loadedCount, setLoadedCount] = useState(false) - const { smartAccountAddress: scwAddress } = useSmartAccount(); + const { smartAccountAddress: scwAddress } = useSmartAccount() const { mutate, data: userOpResponse, error, - isPending, - } = useSendTransaction(); + isPending + } = useSendTransaction() const { isLoading: waitIsLoading, isSuccess: waitIsSuccess, error: waitError, - data: waitData, - } = useUserOpWait(userOpResponse); + data: waitData + } = useUserOpWait(userOpResponse) const getNftCount = useCallback(async () => { - if (!scwAddress || !publicClient) return; + if (!scwAddress || !publicClient) return const nftContract = getContract({ address: config.nft.address as Hex, abi: config.nft.abi, - client: publicClient, - }); - const count = await nftContract.read.balanceOf([scwAddress]); - console.log("count", count); - setNftCount(Number(count)); - }, [publicClient, scwAddress]); + client: publicClient + }) + const count = await nftContract.read.balanceOf([scwAddress]) + console.log("count", count) + setNftCount(Number(count)) + }, [publicClient, scwAddress]) useEffect(() => { if (waitIsSuccess || !loadedCount) { - getNftCount(); - setLoadedCount(true); + getNftCount() + setLoadedCount(true) } - }, [getNftCount, waitIsSuccess]); + }, [getNftCount, waitIsSuccess, loadedCount]) useEffect(() => { waitIsSuccess && - showSuccessMessage( - "Successful mint: " + - `${polygonAmoy.blockExplorers.default.url}/tx/${waitData?.receipt?.transactionHash}` - ); - }, [waitIsSuccess]); + showSuccessMessage(`Successful mint`, waitData?.receipt?.transactionHash) + }, [waitIsSuccess, waitData]) const mintNft = () => mutate({ @@ -66,10 +64,10 @@ const MintNft: React.FC = () => { data: encodeFunctionData({ abi: config.nft.abi, functionName: "safeMint", - args: [scwAddress as Hex], - }), - }, - }); + args: [scwAddress as Hex] + }) + } + }) return (
@@ -96,22 +94,22 @@ const MintNft: React.FC = () => {
- ); -}; + ) +} const useStyles = makeStyles(() => ({ main: { padding: "10px 40px", - color: "#EEEEEE", + color: "#EEEEEE" }, subTitle: { color: "#FFB999", fontSize: 36, - margin: 0, + margin: 0 }, h3Title: { - color: "#e6e6e6", - }, -})); + color: "#e6e6e6" + } +})) -export default MintNft; +export default MintNft diff --git a/src/components/AA/index.tsx b/src/components/AA/index.tsx index 439ee5d..6345df3 100644 --- a/src/components/AA/index.tsx +++ b/src/components/AA/index.tsx @@ -1,11 +1,11 @@ -import React from "react"; -import { makeStyles } from "@mui/styles"; -import ImageIcon from "@mui/icons-material/Image"; +import ImageIcon from "@mui/icons-material/Image" +import { makeStyles } from "@mui/styles" +import type React from "react" interface Props { - useCase: number; - setUseCase: any; - pageIndexChange: any; + useCase: number + setUseCase: any + pageIndexChange: any } const cardItems = [ @@ -18,19 +18,19 @@ const cardItems = [ - ), - }, -]; + ) + } +] const AccountAbstraction: React.FC = ({ useCase: _, setUseCase: __, - pageIndexChange, + pageIndexChange }) => { - const classes = useStyles(); + const classes = useStyles() return (
@@ -58,7 +58,7 @@ const AccountAbstraction: React.FC = ({ {cardItems.map((item, index) => (
pageIndexChange(e, item.index)} - key={index} + key={index + 1} className={classes.card} > {item.icon} @@ -68,7 +68,7 @@ const AccountAbstraction: React.FC = ({ color: "#FFB999", textAlign: "start", fontSize: "auto", - margin: 0, + margin: 0 }} > {item.title} @@ -77,7 +77,7 @@ const AccountAbstraction: React.FC = ({ style={{ fontSize: 14, margin: 0, - textAlign: "center", + textAlign: "center" }} > {item.description} @@ -136,19 +136,19 @@ const AccountAbstraction: React.FC = ({ */}
- ); -}; + ) +} const useStyles = makeStyles(() => ({ main: { padding: "10px 40px", width: "100%", - color: "#e6e6e6", + color: "#e6e6e6" }, subTitle: { color: "#FFB999", fontSize: 36, - margin: 0, + margin: 0 }, textBox: { display: "flex", @@ -157,14 +157,14 @@ const useStyles = makeStyles(() => ({ alignItems: "center", gap: 8, "@media (max-width:1640px)": { - alignItems: "start", - }, + alignItems: "start" + } }, subSubTitle: { fontFamily: "Rubik", color: "#BDC2FF", fontSize: 20, - margin: 20, + margin: 20 }, cardContainer: { display: "flex", @@ -175,8 +175,8 @@ const useStyles = makeStyles(() => ({ gap: 20, cursor: "pointer", "@media (max-width:1640px)": { - flexDirection: "column", - }, + flexDirection: "column" + } }, card: { // width: "25%", @@ -196,9 +196,9 @@ const useStyles = makeStyles(() => ({ width: "100%", maxWidth: "unset", aspectRatio: "unset", - justifyContent: "space-between", - }, - }, -})); + justifyContent: "space-between" + } + } +})) -export default AccountAbstraction; +export default AccountAbstraction diff --git a/src/components/Button.tsx b/src/components/Button.tsx index a79e5cc..f17bcd1 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,23 +1,23 @@ -import React from "react"; -import { makeStyles } from "@mui/styles"; -import { CircularProgress } from "@mui/material"; +import { CircularProgress } from "@mui/material" +import { makeStyles } from "@mui/styles" +import type React from "react" type ButtonProp = { - title: string; - isLoading?: boolean; - onClickFunc: any; - children?: any; - style?: any; -}; + title: string + isLoading?: boolean + onClickFunc: any + children?: any + style?: any +} const Button: React.FC = ({ title, onClickFunc, isLoading = false, children, - style, + style }) => { - const classes = useStyles(); + const classes = useStyles() return ( - ); -}; + ) +} const useStyles = makeStyles((_: any) => ({ btn: { @@ -60,26 +60,26 @@ const useStyles = makeStyles((_: any) => ({ fontSize: 15, "@media (max-width:599px)": { - padding: "0 5px", + padding: "0 5px" }, "&:hover": { - backgroundColor: "#5B3320", + backgroundColor: "#5B3320" }, // disable button "&:disabled": { cursor: "not-allowed", - opacity: 0.5, + opacity: 0.5 }, "& div": { "@media (max-width:599px)": { margin: 0, - display: "none", - }, - }, - }, -})); + display: "none" + } + } + } +})) -export default Button; +export default Button diff --git a/src/components/Faucet/index.tsx b/src/components/Faucet/index.tsx index 1c6d023..90667ee 100644 --- a/src/components/Faucet/index.tsx +++ b/src/components/Faucet/index.tsx @@ -1,34 +1,35 @@ -import React, { useEffect, useState } from "react"; -import { makeStyles } from "@mui/styles"; -import { Hex, encodeFunctionData } from "viem"; +import { makeStyles } from "@mui/styles" +import type React from "react" +import { useEffect, useState } from "react" +import { type Hex, encodeFunctionData } from "viem" -import Button from "../Button"; import { useSendTransaction, useSmartAccount, - useUserOpWait, -} from "@biconomy/use-aa"; -import { configInfo as config, showSuccessMessage } from "../../utils"; -import { ErrorGuard } from "../../utils/ErrorGuard"; -import { polygonAmoy } from "viem/chains"; + useUserOpWait +} from "@biconomy/use-aa" +import { polygonAmoy } from "viem/chains" +import { configInfo as config, showSuccessMessage } from "../../utils" +import { ErrorGuard } from "../../utils/ErrorGuard" +import Button from "../Button" const Faucet: React.FC = () => { - const classes = useStyles(); - const { smartAccountAddress: scwAddress } = useSmartAccount(); - const [address, setAddress] = useState(scwAddress); + const classes = useStyles() + const { smartAccountAddress: scwAddress } = useSmartAccount() + const [address, setAddress] = useState(scwAddress) const { mutate, data: userOpResponse, error, - isPending, - } = useSendTransaction(); + isPending + } = useSendTransaction() const { isSuccess: waitIsSuccess, error: waitError, isLoading: waitIsLoading, - data: waitData, - } = useUserOpWait(userOpResponse); + data: waitData + } = useUserOpWait(userOpResponse) const drip = () => mutate({ @@ -37,18 +38,15 @@ const Faucet: React.FC = () => { data: encodeFunctionData({ abi: config.faucet.abi, functionName: "drip", - args: [address as Hex], - }), - }, - }); + args: [address as Hex] + }) + } + }) useEffect(() => { waitIsSuccess && - showSuccessMessage( - "Successful mint: " + - `${polygonAmoy.blockExplorers.default.url}/tx/${waitData?.receipt?.transactionHash}` - ); - }, [waitIsSuccess]); + showSuccessMessage(`Successful mint`, waitData?.receipt?.transactionHash) + }, [waitIsSuccess, waitData]) return (
@@ -74,8 +72,8 @@ const Faucet: React.FC = () => { />
- ); -}; + ) +} const useStyles = makeStyles(() => ({ main: { @@ -86,35 +84,35 @@ const useStyles = makeStyles(() => ({ color: "#e6e6e6", display: "flex", flexDirection: "column", - alignItems: "start", + alignItems: "start" // justifyContent: "center", }, subTitle: { color: "#FFB999", fontSize: 36, - margin: 0, + margin: 0 }, h3Title: { color: "#FFB999", - margin: 0, + margin: 0 }, container: { // backgroundColor: "rgb(29, 31, 33)", }, containerBtn: { display: "flex", - gap: 15, + gap: 15 // justifyContent: "space-between", }, tab: { padding: "5px 15px", backgroundColor: "#FCF8E8", - marginBottom: 10, + marginBottom: 10 }, listHover: { "&:hover": { - color: "#FF9551", - }, + color: "#FF9551" + } }, input: { maxWidth: 350, @@ -124,8 +122,8 @@ const useStyles = makeStyles(() => ({ outline: "1px solid #5B3320", backgroundColor: "#151520", borderRadius: 6, - border: "none", - }, -})); + border: "none" + } +})) -export default Faucet; +export default Faucet diff --git a/src/components/Forward/MintNft.tsx b/src/components/Forward/MintNft.tsx index d1aae14..ee5e0b1 100644 --- a/src/components/Forward/MintNft.tsx +++ b/src/components/Forward/MintNft.tsx @@ -1,47 +1,48 @@ -import { makeStyles } from "@mui/styles"; -import CircularProgress from "@mui/material/CircularProgress"; -import { PaymasterFeeQuote, PaymasterMode } from "@biconomy/account"; -import React, { useEffect, useMemo, useState } from "react"; +import type { PaymasterFeeQuote } from "@biconomy/account" +import CircularProgress from "@mui/material/CircularProgress" +import { makeStyles } from "@mui/styles" +import type React from "react" +import { useEffect, useMemo, useState } from "react" -import Button from "../Button"; import { Options, mergeOptions, useSendTransaction, useSmartAccount, useTokenFees, - useUserOpWait, -} from "@biconomy/use-aa"; -import { configInfo as config, showSuccessMessage } from "../../utils"; -import { Hex, encodeFunctionData, getContract } from "viem"; -import { usePublicClient } from "wagmi"; -import { ErrorGuard } from "../../utils/ErrorGuard"; -import { polygonAmoy } from "viem/chains"; + useUserOpWait +} from "@biconomy/use-aa" +import { type Hex, encodeFunctionData, getContract } from "viem" +import { polygonAmoy } from "viem/chains" +import { usePublicClient } from "wagmi" +import { configInfo as config, showSuccessMessage } from "../../utils" +import { ErrorGuard } from "../../utils/ErrorGuard" +import Button from "../Button" const MintNftForward: React.FC = () => { - const classes = useStyles(); - const publicClient = usePublicClient(); - const { smartAccountAddress } = useSmartAccount(); - const [nftCount, setNftCount] = useState(null); - const [selectedQuote, setSelectedQuote] = useState(); + const classes = useStyles() + const publicClient = usePublicClient() + const { smartAccountAddress } = useSmartAccount() + const [nftCount, setNftCount] = useState(null) + const [selectedQuote, setSelectedQuote] = useState() useEffect(() => { const getNftCount = async () => { - if (!smartAccountAddress || !publicClient) return; + if (!smartAccountAddress || !publicClient) return const nftContract = getContract({ address: config.nft.address as Hex, abi: config.nft.abi, - client: publicClient, - }); + client: publicClient + }) const count = await nftContract.read.balanceOf([ - smartAccountAddress as Hex, - ]); - console.log("count", Number(count)); - setNftCount(Number(count)); - }; - getNftCount(); + smartAccountAddress as Hex + ]) + console.log("count", Number(count)) + setNftCount(Number(count)) + } + getNftCount() // eslint-disable-next-line react-hooks/exhaustive-deps - }, [smartAccountAddress, publicClient]); + }, [smartAccountAddress, publicClient]) const transactions = useMemo( () => ({ @@ -49,51 +50,48 @@ const MintNftForward: React.FC = () => { data: encodeFunctionData({ abi: config.nft.abi, functionName: "safeMint", - args: [smartAccountAddress as Hex], - }), + args: [smartAccountAddress as Hex] + }) }), [smartAccountAddress] - ); + ) - const { data, isLoading: isLoadingFee } = useTokenFees({ transactions }); + const { data, isLoading: isLoadingFee } = useTokenFees({ transactions }) const { mutate, data: userOpResponse, error, - isPending: isLoading, - } = useSendTransaction(); + isPending: isLoading + } = useSendTransaction() const { isLoading: waitIsLoading, isSuccess: waitIsSuccess, error: waitError, - data: waitData, - } = useUserOpWait(userOpResponse); + data: waitData + } = useUserOpWait(userOpResponse) useEffect(() => { waitIsSuccess && - showSuccessMessage( - "Successful mint: " + - `${polygonAmoy.blockExplorers.default.url}/tx/${waitData?.receipt?.transactionHash}` - ); - }, [waitIsSuccess]); + showSuccessMessage(`Successful mint`, waitData?.receipt?.transactionHash) + }, [waitIsSuccess, waitData]) console.log( mergeOptions([ Options.GasTokenPayment, - Options.getGasTokenFeeQuote(selectedQuote!), + Options.getGasTokenFeeQuote(selectedQuote!) ]) - ); + ) const mintNft = () => { mutate({ transactions, options: mergeOptions([ Options.GasTokenPayment, - Options.getGasTokenFeeQuote(selectedQuote!), - ]), - }); - }; + Options.getGasTokenFeeQuote(selectedQuote!) + ]) + }) + } return (
@@ -123,7 +121,7 @@ const MintNftForward: React.FC = () => { style={{ display: "flex", alignItems: "center", - margin: "0 0 40px 30px", + margin: "0 0 40px 30px" }} > { width: 25, height: 25, marginRight: 10, - color: "#e6e6e6", + color: "#e6e6e6" }} />{" "} {" Loading Fee Options"} @@ -145,7 +143,7 @@ const MintNftForward: React.FC = () => { flexDirection: "column", justifyContent: "start", marginLeft: 0, - gap: 8, + gap: 8 }} > {(data?.feeQuotes ?? []).map((token, ind) => ( @@ -154,7 +152,7 @@ const MintNftForward: React.FC = () => { type="radio" onChange={() => setSelectedQuote(token)} style={{ - color: "#FFB999", + color: "#FFB999" }} name={token.symbol} id={token.symbol} @@ -174,28 +172,28 @@ const MintNftForward: React.FC = () => { />
- ); -}; + ) +} const useStyles = makeStyles(() => ({ main: { margin: "auto", padding: "10px 40px", - color: "#EEEEEE", + color: "#EEEEEE" }, subTitle: { color: "#FFB999", fontSize: 36, - margin: 0, + margin: 0 }, h3Title: { - color: "#e6e6e6", + color: "#e6e6e6" }, listHover: { "&:hover": { - color: "#FF9551", - }, - }, -})); + color: "#FF9551" + } + } +})) -export default MintNftForward; +export default MintNftForward diff --git a/src/components/Forward/index.tsx b/src/components/Forward/index.tsx index 07b744a..d9b6e7c 100644 --- a/src/components/Forward/index.tsx +++ b/src/components/Forward/index.tsx @@ -1,11 +1,11 @@ -import React from "react"; -import { makeStyles } from "@mui/styles"; -import CollectionsIcon from "@mui/icons-material/Collections"; +import CollectionsIcon from "@mui/icons-material/Collections" +import { makeStyles } from "@mui/styles" +import type React from "react" interface Props { - useCase: number; - setUseCase: any; - pageIndexChange: any; + useCase: number + setUseCase: any + pageIndexChange: any } const cardItems = [ @@ -18,19 +18,19 @@ const cardItems = [ - ), - }, -]; + ) + } +] const ForwardFlow: React.FC = ({ useCase: _, setUseCase: __, - pageIndexChange, + pageIndexChange }) => { - const classes = useStyles(); + const classes = useStyles() return (
@@ -52,7 +52,7 @@ const ForwardFlow: React.FC = ({ {cardItems.map((item, index) => (
pageIndexChange(e, item.index)} - key={index} + key={index + 1} className={classes.card} > {item.icon} @@ -62,7 +62,7 @@ const ForwardFlow: React.FC = ({ color: "#FFB999", textAlign: "start", fontSize: "auto", - margin: 0, + margin: 0 }} > {item.title} @@ -71,7 +71,7 @@ const ForwardFlow: React.FC = ({ style={{ fontSize: 14, margin: 0, - textAlign: "center", + textAlign: "center" }} > {item.description} @@ -81,7 +81,7 @@ const ForwardFlow: React.FC = ({ ))}
- ); + ) // return ( //
//

EIP4337: Account Abstraction

@@ -147,18 +147,18 @@ const ForwardFlow: React.FC = ({ // //
// ); -}; +} const useStyles = makeStyles(() => ({ main: { padding: "10px 40px", width: "100%", - color: "#e6e6e6", + color: "#e6e6e6" }, subTitle: { color: "#FFB999", fontSize: 36, - margin: 0, + margin: 0 }, textBox: { display: "flex", @@ -167,14 +167,14 @@ const useStyles = makeStyles(() => ({ alignItems: "center", gap: 8, "@media (max-width:1640px)": { - alignItems: "start", - }, + alignItems: "start" + } }, subSubTitle: { fontFamily: "Rubik", color: "#BDC2FF", fontSize: 20, - margin: 20, + margin: 20 }, cardContainer: { display: "flex", @@ -185,8 +185,8 @@ const useStyles = makeStyles(() => ({ gap: 20, cursor: "pointer", "@media (max-width:1640px)": { - flexDirection: "column", - }, + flexDirection: "column" + } }, card: { // width: "25%", @@ -206,9 +206,9 @@ const useStyles = makeStyles(() => ({ width: "100%", maxWidth: "unset", aspectRatio: "unset", - justifyContent: "space-between", - }, - }, -})); + justifyContent: "space-between" + } + } +})) -export default ForwardFlow; +export default ForwardFlow diff --git a/src/components/Modules/CreateBatchSession.tsx b/src/components/Modules/CreateBatchSession.tsx index 1e3fcbc..5203cb6 100644 --- a/src/components/Modules/CreateBatchSession.tsx +++ b/src/components/Modules/CreateBatchSession.tsx @@ -1,35 +1,39 @@ -import React, { useEffect, useState } from "react"; -import { makeStyles } from "@mui/styles"; -import { useAccount } from "wagmi"; -import Button from "../Button"; +import type { Policy as PolicyFromSDK } from "@biconomy/account" import { Options, bigIntReplacer, mergeOptions, useCreateBatchSession, useSmartAccount, - useUserOpWait, -} from "@biconomy/use-aa"; -import { configInfo, showSuccessMessage } from "../../utils"; -import { polygonAmoy } from "viem/chains"; -import { Hex } from "viem"; -import UseBatchSession from "./UseBatchSession"; -import { ErrorGuard } from "../../utils/ErrorGuard"; -import { Policy as PolicyFromSDK } from "@biconomy/account"; + useUserOpWait +} from "@biconomy/use-aa" +import { makeStyles } from "@mui/styles" +import type React from "react" +import { useEffect } from "react" +import type { Hex } from "viem" +import { useAccount } from "wagmi" +import { configInfo, showSuccessMessage } from "../../utils" +import { ErrorGuard } from "../../utils/ErrorGuard" +import { useHasSession } from "../../utils/useHasSession" +import Button from "../Button" +import UseBatchSession from "./UseBatchSession" -export type Policy = Omit; +export type Policy = Omit const CreateBatchSession: React.FC = () => { - const classes = useStyles(); - const { address } = useAccount(); - const { smartAccountAddress: scwAddress } = useSmartAccount(); - const [hasSession, setHasSession] = useState(false); + const classes = useStyles() + const { address } = useAccount() + const { smartAccountAddress } = useSmartAccount() + const [hasSession, setHasSession] = useState(false) + const canResumeSession = useHasSession(smartAccountAddress, "BATCHED") + + const showUseSession = hasSession || canResumeSession const policyLeaves: Policy[] = [ { interval: { validUntil: 0, - validAfter: 0, + validAfter: 0 }, contractAddress: configInfo.nft.address as Hex, functionSelector: "safeMint(address)", @@ -37,15 +41,15 @@ const CreateBatchSession: React.FC = () => { { offset: 0, condition: 0, - referenceValue: scwAddress, - }, + referenceValue: smartAccountAddress + } ], - valueLimit: 0n, + valueLimit: 0n }, { interval: { validUntil: 0, - validAfter: 0, + validAfter: 0 }, contractAddress: configInfo.nft.address as Hex, functionSelector: "safeMint(address)", @@ -53,45 +57,42 @@ const CreateBatchSession: React.FC = () => { { offset: 0, condition: 0, - referenceValue: scwAddress, - }, + referenceValue: smartAccountAddress + } ], - valueLimit: 0n, - }, - ]; + valueLimit: 0n + } + ] const { mutate, data: userOpResponse, error, - isPending: isLoading, - } = useCreateBatchSession(); + isPending: isLoading + } = useCreateBatchSession() const { isLoading: waitIsLoading, isSuccess: waitIsSuccess, error: waitError, - data: waitData, - } = useUserOpWait(userOpResponse); + data: waitData + } = useUserOpWait(userOpResponse) useEffect(() => { if (waitIsSuccess) { - setHasSession(true); - showSuccessMessage( - "Successful mint: " + - `${polygonAmoy.blockExplorers.default.url}/tx/${waitData?.receipt?.transactionHash}` - ); + setHasSession(true) + showSuccessMessage(`Successful mint`, waitData?.receipt?.transactionHash) } - }, [waitIsSuccess]); + }, [waitIsSuccess, waitData, setHasSession]) const createSessionHandler = () => mutate({ policy: policyLeaves, options: mergeOptions([ Options.Sponsored, - Options.getIncreasedVerification(50), - ]), - }); + Options.getIncreasedVerification(50) + ]) + }) return (
@@ -104,9 +105,9 @@ const CreateBatchSession: React.FC = () => {
policy: {JSON.stringify(policyLeaves, bigIntReplacer, 2)}
- {!!hasSession ? ( + {!!showUseSession ? ( ) : ( @@ -118,22 +119,25 @@ const CreateBatchSession: React.FC = () => { )}
- ); -}; + ) +} const useStyles = makeStyles(() => ({ main: { padding: "10px 40px", - color: "#EEEEEE", + color: "#EEEEEE" }, subTitle: { color: "#FFB999", fontSize: 36, - margin: 0, + margin: 0 }, h3Title: { - color: "#e6e6e6", - }, -})); + color: "#e6e6e6" + } +})) -export default CreateBatchSession; +export default CreateBatchSession +function useState(arg0: boolean): [any, any] { + throw new Error("Function not implemented.") +} diff --git a/src/components/Modules/CreateDistributedSession.tsx b/src/components/Modules/CreateDistributedSession.tsx new file mode 100644 index 0000000..3d09d36 --- /dev/null +++ b/src/components/Modules/CreateDistributedSession.tsx @@ -0,0 +1,140 @@ +import type { PolicyLeaf } from "@biconomy/account" +import { + Options, + bigIntReplacer, + useCreateSessionWithDistributedKey, + useSmartAccount, + useUserOpWait +} from "@biconomy/use-aa" +import { makeStyles } from "@mui/styles" +import type React from "react" +import { useEffect, useState } from "react" +import { ToastContainer } from "react-toastify" +import "react-toastify/dist/ReactToastify.css" +import type { Hex } from "viem" +import { showSuccessMessage } from "../../utils" +import { ErrorGuard } from "../../utils/ErrorGuard" +import { useHasSession } from "../../utils/useHasSession" +import Button from "../Button" +import UseDistributedSession from "./UseDistributedSession" + +const CreateDistributedSession: React.FC = () => { + const classes = useStyles() + const nftAddress: Hex = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e" + const { smartAccountAddress } = useSmartAccount() + const canResumeSession = useHasSession(smartAccountAddress, "DISTRIBUTED_KEY") + const [hasSession, setHasSession] = useState(false) + + const showUseSession = hasSession || canResumeSession + + const policy: PolicyLeaf[] = [ + { + contractAddress: nftAddress, + functionSelector: "safeMint(address)", + rules: [ + { + offset: 0, + condition: 0, + referenceValue: smartAccountAddress + } + ], + interval: { + validUntil: 0, + validAfter: 0 + }, + valueLimit: 0n + } + ] + + const { + mutate, + data: userOpResponse, + error, + isPending + } = useCreateSessionWithDistributedKey() + + const { + isLoading: waitIsLoading, + isSuccess: waitIsSuccess, + error: waitError, + data: waitData + } = useUserOpWait(userOpResponse) + + const isLoading = waitIsLoading || isPending + + useEffect(() => { + if (waitIsSuccess) { + setHasSession(true) + showSuccessMessage(`Successful mint`, waitData?.receipt?.transactionHash) + } + }, [waitIsSuccess, waitData]) + + const createDistributedSessionHandler = () => + mutate({ + policy, + options: Options.Sponsored + }) + + return ( +
+ +

+ Use Cases {"->"} Modules {"->"} {!!showUseSession ? "Use" : "Create"}{" "} + Dan Session +

+ +

+ {!!showUseSession ? "Use" : "Create"} a Dan Session +

+ + + +
policy: {JSON.stringify(policy, bigIntReplacer, 2)}
+ + {!!showUseSession ? ( + + ) : ( +
+ ) +} + +const useStyles = makeStyles(() => ({ + main: { + margin: "auto", + padding: "10px 40px", + color: "#EEEEEE" + }, + subTitle: { + color: "#FFB999", + fontSize: 36, + margin: 0 + }, + h3Title: { + color: "#e6e6e6" + }, + listHover: { + "&:hover": { + color: "#FF9551" + } + } +})) + +export default CreateDistributedSession diff --git a/src/components/Modules/CreateSession.tsx b/src/components/Modules/CreateSession.tsx index 3f91ca6..7b7542b 100644 --- a/src/components/Modules/CreateSession.tsx +++ b/src/components/Modules/CreateSession.tsx @@ -1,30 +1,31 @@ -import React, { useEffect, useState } from "react"; -import { ToastContainer } from "react-toastify"; -import "react-toastify/dist/ReactToastify.css"; -import UseSession from "./UseSession"; -import { useAccount } from "wagmi"; import { Options, bigIntReplacer, useCreateSession, useSmartAccount, - useUserOpWait, -} from "@biconomy/use-aa"; -import Button from "../Button"; -import { makeStyles } from "@mui/styles"; -import { Hex } from "viem"; -import { ErrorGuard } from "../../utils/ErrorGuard"; -import { showSuccessMessage } from "../../utils"; -import { polygonAmoy } from "viem/chains"; + useUserOpWait +} from "@biconomy/use-aa" +import { makeStyles } from "@mui/styles" +import type React from "react" +import { useEffect, useReducer, useState } from "react" +import { ToastContainer } from "react-toastify" +import "react-toastify/dist/ReactToastify.css" +import type { Hex } from "viem" +import { showSuccessMessage } from "../../utils" +import { ErrorGuard } from "../../utils/ErrorGuard" +import { useHasSession } from "../../utils/useHasSession" +import Button from "../Button" +import UseSession from "./UseSession" const CreateSession: React.FC = () => { - const classes = useStyles(); + const classes = useStyles() - const nftAddress: Hex = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e"; + const nftAddress: Hex = "0x1758f42Af7026fBbB559Dc60EcE0De3ef81f665e" + const { smartAccountAddress: scwAddress } = useSmartAccount() + const canResumeSession = useHasSession(scwAddress, "STANDARD") + const [hasSession, setHasSession] = useState(false) - const [hasSession, setHasSession] = useState(false); - const { address } = useAccount(); - const { smartAccountAddress: scwAddress } = useSmartAccount(); + const showUseSession = hasSession || canResumeSession const policy = [ { @@ -34,57 +35,54 @@ const CreateSession: React.FC = () => { { offset: 0, condition: 0, - referenceValue: scwAddress, - }, + referenceValue: scwAddress + } ], interval: { validUntil: 0, - validAfter: 0, + validAfter: 0 }, - valueLimit: 0n, - }, - ]; + valueLimit: 0n + } + ] const { mutate, data: userOpResponse, error, - isPending: isLoading, - } = useCreateSession(); + isPending: isLoading + } = useCreateSession() const { isLoading: waitIsLoading, isSuccess: waitIsSuccess, error: waitError, - data: waitData, - } = useUserOpWait(userOpResponse); + data: waitData + } = useUserOpWait(userOpResponse) useEffect(() => { if (waitIsSuccess) { - setHasSession(true); - showSuccessMessage( - "Successful mint: " + - `${polygonAmoy.blockExplorers.default.url}/tx/${waitData?.receipt?.transactionHash}` - ); + setHasSession(true) + showSuccessMessage(`Successful mint`, waitData?.receipt?.transactionHash) } - }, [waitIsSuccess]); + }, [waitIsSuccess, waitData]) const createSessionHandler = () => mutate({ policy, - options: Options.Sponsored, - }); + options: Options.Sponsored + }) return (

- Use Cases {"->"} Modules {"->"} {hasSession ? "Use" : "Create"}{" "} + Use Cases {"->"} Modules {"->"} {showUseSession ? "Use" : "Create"}{" "} Session

- {hasSession ? "Use" : "Create"} a Session + {showUseSession ? "Use" : "Create"} a Session

{
policy: {JSON.stringify(policy, bigIntReplacer, 2)}
- {!!hasSession ? ( - + {showUseSession ? ( + ) : (
- ); -}; + ) +} const useStyles = makeStyles(() => ({ main: { margin: "auto", padding: "10px 40px", - color: "#EEEEEE", + color: "#EEEEEE" }, subTitle: { color: "#FFB999", fontSize: 36, - margin: 0, + margin: 0 }, h3Title: { - color: "#e6e6e6", + color: "#e6e6e6" }, listHover: { "&:hover": { - color: "#FF9551", - }, - }, -})); + color: "#FF9551" + } + } +})) -export default CreateSession; +export default CreateSession diff --git a/src/components/Modules/UseBatchSession.tsx b/src/components/Modules/UseBatchSession.tsx index ed50a8f..d5c61b8 100644 --- a/src/components/Modules/UseBatchSession.tsx +++ b/src/components/Modules/UseBatchSession.tsx @@ -1,16 +1,16 @@ -import React, { useEffect } from "react"; -import { Transaction } from "@biconomy/account"; -import "react-toastify/dist/ReactToastify.css"; -import { Hex, encodeFunctionData, parseAbi } from "viem"; -import Button from "../Button"; -import { configInfo, showSuccessMessage } from "../../utils"; -import { polygonAmoy } from "viem/chains"; -import { useBatchSession, useUserOpWait, Options } from "@biconomy/use-aa"; -import { ErrorGuard } from "../../utils/ErrorGuard"; +import type { Transaction } from "@biconomy/account" +import { Options, useBatchSession, useUserOpWait } from "@biconomy/use-aa" +import type React from "react" +import { useEffect } from "react" +import "react-toastify/dist/ReactToastify.css" +import { type Hex, encodeFunctionData, parseAbi } from "viem" +import { configInfo, showSuccessMessage } from "../../utils" +import { ErrorGuard } from "../../utils/ErrorGuard" +import Button from "../Button" interface props { - smartAccountAddress?: Hex; - address?: string; + smartAccountAddress?: Hex + address?: string } const UseBatchSession: React.FC = ({ smartAccountAddress }) => { @@ -18,40 +18,38 @@ const UseBatchSession: React.FC = ({ smartAccountAddress }) => { mutate, data: userOpResponse, error, - isPending: isLoading, - } = useBatchSession(); + isPending: isLoading + } = useBatchSession() const { isLoading: waitIsLoading, isSuccess: waitIsSuccess, error: waitError, - data: waitData, - } = useUserOpWait(userOpResponse); + data: waitData + } = useUserOpWait(userOpResponse) const nftMintTx: Transaction = { to: configInfo.nft.address, data: encodeFunctionData({ abi: parseAbi(["function safeMint(address _to)"]), functionName: "safeMint", - args: [smartAccountAddress as Hex], - }), - }; + args: [smartAccountAddress as Hex] + }) + } const txTwice = () => mutate({ transactions: [nftMintTx, nftMintTx], correspondingIndexes: [0, 1], options: Options.getIncreasedVerification(50), - }); + smartAccountAddress + }) useEffect(() => { if (waitIsSuccess) { - showSuccessMessage( - "Successful mint: " + - `${polygonAmoy.blockExplorers.default.url}/tx/${waitData?.receipt?.transactionHash}` - ); + showSuccessMessage(`Successful mint`, waitData?.receipt?.transactionHash) } - }, [waitIsSuccess]); + }, [waitIsSuccess, waitData]) return ( @@ -61,7 +59,7 @@ const UseBatchSession: React.FC = ({ smartAccountAddress }) => { isLoading={isLoading || waitIsLoading} /> - ); -}; + ) +} -export default UseBatchSession; +export default UseBatchSession diff --git a/src/components/Modules/UseDistributedSession.tsx b/src/components/Modules/UseDistributedSession.tsx new file mode 100644 index 0000000..1582c15 --- /dev/null +++ b/src/components/Modules/UseDistributedSession.tsx @@ -0,0 +1,64 @@ +import { + Options, + useSessionWithDistributedKey, + useUserOpWait +} from "@biconomy/use-aa" +import type React from "react" +import { useEffect } from "react" +import "react-toastify/dist/ReactToastify.css" +import { type Address, type Hex, encodeFunctionData, parseAbi } from "viem" +import { configInfo, showSuccessMessage } from "../../utils" +import { ErrorGuard } from "../../utils/ErrorGuard" +import Button from "../Button" + +interface props { + smartAccountAddress: Address +} + +const UseDistributedSession: React.FC = ({ smartAccountAddress }) => { + const { + mutate, + data: userOpResponse, + error, + isPending: isLoading + } = useSessionWithDistributedKey() + + const { + isLoading: waitIsLoading, + isSuccess: waitIsSuccess, + error: waitError, + data: waitData + } = useUserOpWait(userOpResponse) + + const mintTx = () => + mutate({ + transactions: { + to: configInfo.nft.address, + data: encodeFunctionData({ + abi: parseAbi(["function safeMint(address _to)"]), + functionName: "safeMint", + args: [smartAccountAddress as Hex] + }) + }, + options: Options.Sponsored, + smartAccountAddress + }) + + useEffect(() => { + if (waitIsSuccess) { + showSuccessMessage(`Successful mint`, waitData?.receipt?.transactionHash) + } + }, [waitIsSuccess, waitData]) + + return ( + +