diff --git a/front/src/app/api/users/[id]/txs/route.ts b/front/src/app/api/users/[id]/txs/route.ts deleted file mode 100644 index 9afc901..0000000 --- a/front/src/app/api/users/[id]/txs/route.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ENTRYPOINT_ADDRESS, FACTORY_ABI, FACTORY_ADDRESS, PUBLIC_CLIENT } from "@/constants"; -import { Hex, stringify } from "viem"; - -export async function GET(_req: Request, { params }: { params: { id: Hex } }) { - const { id } = params; - - const user = await PUBLIC_CLIENT.readContract({ - address: FACTORY_ADDRESS, - abi: FACTORY_ABI, - functionName: "getUser", - args: [BigInt(id)], - }); - - const latestBlock = await PUBLIC_CLIENT.getBlock(); - - const logs = await PUBLIC_CLIENT.getLogs({ - address: ENTRYPOINT_ADDRESS, - event: { - inputs: [ - { - internalType: "bytes32", - name: "userOpHash", - type: "bytes32", - indexed: true, - }, - { - internalType: "address", - name: "sender", - type: "address", - indexed: true, - }, - { - internalType: "address", - name: "paymaster", - type: "address", - indexed: true, - }, - { - internalType: "uint256", - name: "nonce", - type: "uint256", - indexed: false, - }, - { - internalType: "bool", - name: "success", - type: "bool", - indexed: false, - }, - { - internalType: "uint256", - name: "actualGasCost", - type: "uint256", - indexed: false, - }, - { - internalType: "uint256", - name: "actualGasUsed", - type: "uint256", - indexed: false, - }, - ], - type: "event", - name: "UserOperationEvent", - anonymous: false, - }, - fromBlock: latestBlock.number - BigInt(2000), - toBlock: latestBlock.number, - args: { sender: user.account }, - }); - - return Response.json(JSON.parse(stringify({ logs }))); -} diff --git a/front/src/app/layout.tsx b/front/src/app/layout.tsx index 60b4013..a020490 100644 --- a/front/src/app/layout.tsx +++ b/front/src/app/layout.tsx @@ -7,7 +7,6 @@ import { SmartWalletProvider } from "@/libs/smart-wallet/SmartWalletProvider"; import { ModalProvider } from "@/providers/ModalProvider"; import { BalanceProvider } from "@/providers/BalanceProvider"; import { MeProvider } from "@/providers/MeProvider"; -import { TransactionProvider } from "@/providers/TransactionProvider"; import { Metadata } from "next"; import { ModalOnWCEvent } from "@/libs/wallet-connect/ModalOnWCEvent"; @@ -36,21 +35,19 @@ export default function RootLayout({ children }: { children: React.ReactNode }) - - - - - - - - {children} - - - - - - - + + + + + + + {children} + + + + + + diff --git a/front/src/components/Address/index.tsx b/front/src/components/Address/index.tsx index 3a2c182..056aaf1 100644 --- a/front/src/components/Address/index.tsx +++ b/front/src/components/Address/index.tsx @@ -25,6 +25,7 @@ export default function Address(props: Props) { setTimeout(() => setIsCopied(false), 1000); }} style={{ ...props.style, display: "flex", alignItems: "center", gap: "6px" }} + size={"3"} > {me?.account.slice(0, 6)}...{me?.account.slice(-4)} diff --git a/front/src/components/Balance/index.tsx b/front/src/components/Balance/index.tsx index ea6b3d0..ae0ccce 100644 --- a/front/src/components/Balance/index.tsx +++ b/front/src/components/Balance/index.tsx @@ -5,7 +5,7 @@ import { Flex, Text } from "@radix-ui/themes"; import { CSSProperties, useEffect } from "react"; const css: CSSProperties = { - padding: "2.5rem 0", + padding: "4rem 0", }; export default function Balance() { diff --git a/front/src/components/History/index.tsx b/front/src/components/History/index.tsx index 8b27896..5926536 100644 --- a/front/src/components/History/index.tsx +++ b/front/src/components/History/index.tsx @@ -1,57 +1,33 @@ "use client"; -import { useTransaction } from "@/providers/TransactionProvider"; -import { Badge, Box, Button, Flex, Link, Separator, Text } from "@radix-ui/themes"; -import { Log } from "viem"; +import { Button, Flex, Callout } from "@radix-ui/themes"; +import { useMe } from "@/providers/MeProvider"; +import { ArrowRightIcon } from "@radix-ui/react-icons"; +import LogoAnimatedLight from "../LogoAnimatedLight"; export default function History() { - const { loading, txs, getLastTxs, unwatchLogs } = useTransaction(); - - if (loading) - return ( - - Fetching latest transactions... - - ); + const { me } = useMe(); return ( - - - - History - - - - - - {!loading && - Array.isArray(txs) && - txs.map((tx: Log) => { - return ( - - - - - {tx?.transactionHash?.toString().slice(0, 4)}... - {tx?.transactionHash?.toString().slice(-4)} - - - - {(tx as unknown as { args: { status: boolean } }).args.status} - - {(tx as unknown as { args: { success: boolean } })?.args.success ? ( - Complete - ) : ( - Failed - )} - - - ); - })} + + + + You smart contract wallet is deployed during the first transaction that you make. You can + still receive tokens and ETH on your smart contract wallet address in the meantime. + + + - + ); } diff --git a/front/src/components/HomePage/index.tsx b/front/src/components/HomePage/index.tsx index 0fb8c59..96710ef 100644 --- a/front/src/components/HomePage/index.tsx +++ b/front/src/components/HomePage/index.tsx @@ -7,6 +7,7 @@ import Balance from "../Balance"; import NavBar from "../NavBar"; import History from "../History"; import TopBar from "../TopBar"; +import LogoAnimated from "../LogoAnimated"; export default function Home() { const { me, isMounted } = useMe(); @@ -15,10 +16,12 @@ export default function Home() { if (me) { return ( - - - - + + + + + + ); diff --git a/front/src/components/LogoAnimatedLight/index.tsx b/front/src/components/LogoAnimatedLight/index.tsx new file mode 100644 index 0000000..963f7ec --- /dev/null +++ b/front/src/components/LogoAnimatedLight/index.tsx @@ -0,0 +1,75 @@ +import { CSSProperties, useRef, useState } from "react"; +import "./logo-animated.css"; + +type Props = { + style?: CSSProperties; +}; + +export default function LogoAnimatedLight({ style }: Props) { + const [increment, setIncrement] = useState(0); + + return ( + setIncrement(increment + 1)} + > + + + + + + + + + + + + + ); +} diff --git a/front/src/components/LogoAnimatedLight/logo-animated.css b/front/src/components/LogoAnimatedLight/logo-animated.css new file mode 100644 index 0000000..d3dd35e --- /dev/null +++ b/front/src/components/LogoAnimatedLight/logo-animated.css @@ -0,0 +1,464 @@ +/*************************************************** + * Generated by SVG Artista on 11/17/2023, 9:24:10 AM + * MIT license (https://opensource.org/licenses/MIT) + * W. https://svgartista.net + **************************************************/ + + @-webkit-keyframes animate-svg-stroke-1 { + 0% { + stroke-dashoffset: 2868.76611328125px; + stroke-dasharray: 2868.76611328125px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 2868.76611328125px; + } +} + +@keyframes animate-svg-stroke-1 { + 0% { + stroke-dashoffset: 2868.76611328125px; + stroke-dasharray: 2868.76611328125px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 2868.76611328125px; + } +} + +@-webkit-keyframes animate-svg-fill-1 { + 0% { + fill: transparent; + } + + 100% { + fill:var(--accent-11); + } +} + +@keyframes animate-svg-fill-1 { + 0% { + fill: transparent; + } + + 100% { + fill:var(--accent-11); + } +} + +.svg-elem-1 { + -webkit-animation: animate-svg-stroke-1 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0s both, + animate-svg-fill-1 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8s both; + animation: animate-svg-stroke-1 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0s both, + animate-svg-fill-1 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8s both; +} + +@-webkit-keyframes animate-svg-stroke-2 { + 0% { + stroke-dashoffset: 2868.76611328125px; + stroke-dasharray: 2868.76611328125px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 2868.76611328125px; + } +} + +@keyframes animate-svg-stroke-2 { + 0% { + stroke-dashoffset: 2868.76611328125px; + stroke-dasharray: 2868.76611328125px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 2868.76611328125px; + } +} + +@-webkit-keyframes animate-svg-fill-2 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +@keyframes animate-svg-fill-2 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +.svg-elem-2 { + -webkit-animation: animate-svg-stroke-2 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.01s both, + animate-svg-fill-2 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.81s both; + animation: animate-svg-stroke-2 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.01s both, + animate-svg-fill-2 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.81s both; +} + +@-webkit-keyframes animate-svg-stroke-3 { + 0% { + stroke-dashoffset: 7424.7177734375px; + stroke-dasharray: 7424.7177734375px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 7424.7177734375px; + } +} + +@keyframes animate-svg-stroke-3 { + 0% { + stroke-dashoffset: 7424.7177734375px; + stroke-dasharray: 7424.7177734375px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 7424.7177734375px; + } +} + +@-webkit-keyframes animate-svg-fill-3 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +@keyframes animate-svg-fill-3 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +.svg-elem-3 { + -webkit-animation: animate-svg-stroke-3 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.02s both, + animate-svg-fill-3 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8200000000000001s both; + animation: animate-svg-stroke-3 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.02s both, + animate-svg-fill-3 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8200000000000001s both; +} + +@-webkit-keyframes animate-svg-stroke-4 { + 0% { + stroke-dashoffset: 1231.14892578125px; + stroke-dasharray: 1231.14892578125px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 1231.14892578125px; + } +} + +@keyframes animate-svg-stroke-4 { + 0% { + stroke-dashoffset: 1231.14892578125px; + stroke-dasharray: 1231.14892578125px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 1231.14892578125px; + } +} + +@-webkit-keyframes animate-svg-fill-4 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +@keyframes animate-svg-fill-4 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +.svg-elem-4 { + -webkit-animation: animate-svg-stroke-4 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.03s both, + animate-svg-fill-4 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8300000000000001s both; + animation: animate-svg-stroke-4 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.03s both, + animate-svg-fill-4 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8300000000000001s both; +} + +@-webkit-keyframes animate-svg-stroke-5 { + 0% { + stroke-dashoffset: 1266.6910400390625px; + stroke-dasharray: 1266.6910400390625px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 1266.6910400390625px; + } +} + +@keyframes animate-svg-stroke-5 { + 0% { + stroke-dashoffset: 1266.6910400390625px; + stroke-dasharray: 1266.6910400390625px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 1266.6910400390625px; + } +} + +@-webkit-keyframes animate-svg-fill-5 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +@keyframes animate-svg-fill-5 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +.svg-elem-5 { + -webkit-animation: animate-svg-stroke-5 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.04s both, + animate-svg-fill-5 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8400000000000001s both; + animation: animate-svg-stroke-5 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.04s both, + animate-svg-fill-5 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8400000000000001s both; +} + +@-webkit-keyframes animate-svg-stroke-6 { + 0% { + stroke-dashoffset: 132.3062286376953px; + stroke-dasharray: 132.3062286376953px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 132.3062286376953px; + } +} + +@keyframes animate-svg-stroke-6 { + 0% { + stroke-dashoffset: 132.3062286376953px; + stroke-dasharray: 132.3062286376953px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 132.3062286376953px; + } +} + +@-webkit-keyframes animate-svg-fill-6 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +@keyframes animate-svg-fill-6 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +.svg-elem-6 { + -webkit-animation: animate-svg-stroke-6 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.05s both, + animate-svg-fill-6 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8500000000000001s both; + animation: animate-svg-stroke-6 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.05s both, + animate-svg-fill-6 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8500000000000001s both; +} + +@-webkit-keyframes animate-svg-stroke-7 { + 0% { + stroke-dashoffset: 177.990478515625px; + stroke-dasharray: 177.990478515625px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 177.990478515625px; + } +} + +@keyframes animate-svg-stroke-7 { + 0% { + stroke-dashoffset: 177.990478515625px; + stroke-dasharray: 177.990478515625px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 177.990478515625px; + } +} + +@-webkit-keyframes animate-svg-fill-7 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +@keyframes animate-svg-fill-7 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +.svg-elem-7 { + -webkit-animation: animate-svg-stroke-7 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.06s both, + animate-svg-fill-7 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8600000000000001s both; + animation: animate-svg-stroke-7 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.06s both, + animate-svg-fill-7 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8600000000000001s both; +} + +@-webkit-keyframes animate-svg-stroke-8 { + 0% { + stroke-dashoffset: 278.4589538574219px; + stroke-dasharray: 278.4589538574219px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 278.4589538574219px; + } +} + +@keyframes animate-svg-stroke-8 { + 0% { + stroke-dashoffset: 278.4589538574219px; + stroke-dasharray: 278.4589538574219px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 278.4589538574219px; + } +} + +@-webkit-keyframes animate-svg-fill-8 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +@keyframes animate-svg-fill-8 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +.svg-elem-8 { + -webkit-animation: animate-svg-stroke-8 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.07s both, + animate-svg-fill-8 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8700000000000001s both; + animation: animate-svg-stroke-8 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.07s both, + animate-svg-fill-8 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.8700000000000001s both; +} + +@-webkit-keyframes animate-svg-stroke-9 { + 0% { + stroke-dashoffset: 239.96914672851562px; + stroke-dasharray: 239.96914672851562px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 239.96914672851562px; + } +} + +@keyframes animate-svg-stroke-9 { + 0% { + stroke-dashoffset: 239.96914672851562px; + stroke-dasharray: 239.96914672851562px; + } + + 100% { + stroke-dashoffset: 0; + stroke-dasharray: 239.96914672851562px; + } +} + +@-webkit-keyframes animate-svg-fill-9 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +@keyframes animate-svg-fill-9 { + 0% { + fill: transparent; + } + + 100% { + fill: var(--accent-11); + } +} + +.svg-elem-9 { + -webkit-animation: animate-svg-stroke-9 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.08s both, + animate-svg-fill-9 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.88s both; + animation: animate-svg-stroke-9 1s cubic-bezier(0.47, 0, 0.745, 0.715) 0.08s both, + animate-svg-fill-9 0.7s cubic-bezier(0.47, 0, 0.745, 0.715) 0.88s both; +} diff --git a/front/src/components/NavBar/index.tsx b/front/src/components/NavBar/index.tsx index 74d427e..7e96391 100644 --- a/front/src/components/NavBar/index.tsx +++ b/front/src/components/NavBar/index.tsx @@ -11,21 +11,32 @@ export default function NavBar() { const { open } = useModal(); return ( - + diff --git a/front/src/components/OnBoarding/index.tsx b/front/src/components/OnBoarding/index.tsx index 33f4d1f..2efc5e9 100644 --- a/front/src/components/OnBoarding/index.tsx +++ b/front/src/components/OnBoarding/index.tsx @@ -23,18 +23,11 @@ export default function OnBoarding() { window.open("https://github.com/passkeys-4337/smart-wallet", "_blank")} variant="soft" + size={"3"} > - - Build for - - + diff --git a/front/src/components/SettingsPage/index.tsx b/front/src/components/SettingsPage/index.tsx index aac138e..d02fa26 100644 --- a/front/src/components/SettingsPage/index.tsx +++ b/front/src/components/SettingsPage/index.tsx @@ -27,7 +27,7 @@ export default function SettingsPage() { - + diff --git a/front/src/components/ThemeButton/index.tsx b/front/src/components/ThemeButton/index.tsx index 99c9872..5135335 100644 --- a/front/src/components/ThemeButton/index.tsx +++ b/front/src/components/ThemeButton/index.tsx @@ -22,6 +22,7 @@ export default function ThemeButton({ style }: Props) { { if (theme === "system") { setTheme(systemTheme === "dark" ? "light" : "dark"); diff --git a/front/src/components/TopBar/index.tsx b/front/src/components/TopBar/index.tsx index 729bef9..2717a61 100644 --- a/front/src/components/TopBar/index.tsx +++ b/front/src/components/TopBar/index.tsx @@ -8,7 +8,7 @@ export default function TopBar() {
- + diff --git a/front/src/providers/TransactionProvider/index.tsx b/front/src/providers/TransactionProvider/index.tsx deleted file mode 100644 index abb3b10..0000000 --- a/front/src/providers/TransactionProvider/index.tsx +++ /dev/null @@ -1,126 +0,0 @@ -"use client"; - -import { PUBLIC_CLIENT, ENTRYPOINT_ADDRESS } from "@/constants"; -import { Me, useMe } from "@/providers/MeProvider"; -import { createContext, useCallback, useContext, useEffect, useState } from "react"; -import { Hex, Log } from "viem"; - -const useTxHook = () => { - const [txs, setTxs] = useState | null>(null); - const [loading, setLoading] = useState(false); - - const { me } = useMe(); - - const getLastTxs = useCallback(async (keyId: Hex) => { - setLoading(true); - const res = await fetch(`/api/users/${keyId}/txs`); - const resJson = await res.json(); - setTxs((prev) => { - const logs = resJson.logs.sort((a: Log, b: Log) => { - return Number(b.blockNumber) - Number(a.blockNumber); - }); - if (!prev) return logs; - return [...prev, ...logs]; - }); - setLoading(false); - }, []); - - const unwatchLogs = PUBLIC_CLIENT.watchContractEvent({ - address: ENTRYPOINT_ADDRESS, - abi: [ - { - inputs: [ - { - internalType: "bytes32", - name: "userOpHash", - type: "bytes32", - indexed: true, - }, - { - internalType: "address", - name: "sender", - type: "address", - indexed: true, - }, - { - internalType: "address", - name: "paymaster", - type: "address", - indexed: true, - }, - { - internalType: "uint256", - name: "nonce", - type: "uint256", - indexed: false, - }, - { - internalType: "bool", - name: "success", - type: "bool", - indexed: false, - }, - { - internalType: "uint256", - name: "actualGasCost", - type: "uint256", - indexed: false, - }, - { - internalType: "uint256", - name: "actualGasUsed", - type: "uint256", - indexed: false, - }, - ], - type: "event", - name: "UserOperationEvent", - anonymous: false, - }, - ], - eventName: "UserOperationEvent", - args: { sender: me?.account }, - onLogs: (logs) => { - setLoading(true); - setTxs((prev) => { - if (!prev) return logs; - return [ - ...prev, - ...logs.sort((a: Log, b: Log) => { - return Number(b.blockNumber) - Number(a.blockNumber); - }), - ]; - }); - setLoading(false); - }, - }); - - useEffect(() => { - if (!me) return; - getLastTxs(me?.keyId); - }, [me, getLastTxs]); - - return { - loading, - txs, - getLastTxs, - unwatchLogs, - }; -}; - -type UseTxHook = ReturnType; -const TransactionContext = createContext(null); - -export const useTransaction = (): UseTxHook => { - const context = useContext(TransactionContext); - if (!context) { - throw new Error("useTxHook must be used within a TransactionProvider"); - } - return context; -}; - -export function TransactionProvider({ children }: { children: React.ReactNode }) { - const hook = useTxHook(); - - return {children}; -}