Skip to content
This repository was archived by the owner on Nov 20, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4,208 changes: 4,208 additions & 0 deletions abis/Passkey.json

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions app/add-key/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { NextPage } from "next";
import AddKeyPage from "@/templates/AddKeyPage";

const AddKey: NextPage = () => {
return <AddKeyPage />;
};

export default AddKey;
2 changes: 1 addition & 1 deletion app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export default function Home() {
return;
}
if (!wallet) {
route.push("/create");
route.push("/welcome");
return;
}
}, [wallet, loading, route]);
Expand Down
168 changes: 168 additions & 0 deletions templates/AddKeyPage/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
"use client";
import { Communicator } from "@abstraction-hq/wallet-sdk";
import { useWalletStore } from "@/stores/walletStore";
import type { NextPage } from "next";
import React, { useEffect, useState } from "react";
import { useSearchParams } from "next/navigation";
import SignUpHandle from "../CreatePage/SignUpHandle";
import Image from "@/components/Image";
import Icon from "@/components/Icon";
import Tooltip from "@/components/Tooltip";
import Loading from "@/components/Loading";
import PasskeyAccount from "@/account/passkeyAccount";
import { ethClient } from "@/config";
import { PASSKEY } from "@/constants";
import { encodeFunctionData } from "viem";
import Passkey from "@/abis/Passkey.json"
import toast from "react-hot-toast";
import { submitUserOp } from "@/utils/bundler";

const AddKeyPage: NextPage = () => {
const loading = useWalletStore((state) => state.loading);
const wallet = useWalletStore((state) => state.wallets[state.activeWallet]);
const searchParams = useSearchParams();

const passkeyId = searchParams.get("passkeyId");
const x = searchParams.get("x");
const y = searchParams.get("y");
const device = searchParams.get("device");

const onConnect = async () => {
const account = new PasskeyAccount(
wallet.passkeyCredentialId || "",
0n,
0n
);

const [userOp, userOpHash] = await account.sendTransactionOperation(
ethClient,
[
{
target: PASSKEY,
value: 0,
data: encodeFunctionData({
abi: Passkey.abi,
functionName: "registerPublicKey",
args: [passkeyId, x, y],
}),
},
]
);

toast.promise(submitUserOp(userOp), {
loading: "Sending...",
success: (data) => <div>Transaction Success - <a href={`https://vicscan.xyz/tx/${data.userOpHash}`} target="_blank">Click to view on scan</a></div>,
error: (err) => <div>Transaction Fail - <a href={`https://vicscan.xyz/tx/${err.userOpHash}`} target="_blank">Click to view on scan</a></div>,
})

window.close();
};

if (loading) {
return (
<div className="flex items-center justify-center h-screen">
<Loading />
</div>
);
}

if (!wallet) {
return <div>Wallet not found</div>;
}

return (
<div className="flex justify-center items-center min-h-screen">
<div className="max-w-[28.5rem] w-full p-6 text-white">
<div className="mb-4 text-xl text-center font-semibold text-theme-primary">
Login Request
</div>
<div className="mb-3 text-sm">
<div className="flex items-center border-t border-theme-stroke py-3">
<div className="flex flex-col text-left w-4/5">
<div className="flex items-center">
<div className="text-base-1 text-theme-secondary">
Accounts connect
</div>
</div>
<div className="text-base-1s font-medium text-theme-primary">
{wallet?.senderAddress}
</div>
</div>
<div className="flex justify-end w-1/5">
<Icon
className="fill-theme-primary md:ml-1.5"
name="arrow-next"
/>
</div>
</div>
</div>
<div className="mb-3 text-sm">
<div className="flex items-center border-t border-theme-stroke py-3">
<div className="flex flex-col text-left w-4/5">
<div className="flex items-center">
<div className="text-base-1 text-theme-secondary">
Passkey Id
</div>
</div>
<div className="text-base-1s font-medium text-theme-primary">
{passkeyId}
</div>
</div>
</div>
</div>
<div className="mb-3 text-sm">
<div className="flex items-center border-t border-theme-stroke py-3">
<div className="flex flex-col text-left w-4/5">
<div className="flex items-center">
<div className="text-base-1 text-theme-secondary">
Passkey X
</div>
</div>
<div className="text-base-1s font-medium text-theme-primary">
{x}
</div>
</div>
</div>
</div>
<div className="mb-3 text-sm">
<div className="flex items-center border-t border-theme-stroke py-3">
<div className="flex flex-col text-left w-4/5">
<div className="flex items-center">
<div className="text-base-1 text-theme-secondary">
Passkey Y
</div>
</div>
<div className="text-base-1s font-medium text-theme-primary">
{y}
</div>
</div>
</div>
</div>
<div className="border-t border-theme-stroke mt-3 py-3">
<div className="flex items-center">
<div className="flex justify-start">
<Tooltip className="-mb-0.25 md:mb-0" title={"Tip"} />
</div>
<div className="flex flex-col w-4/5 ml-4 text-left text-red-500">
<div className="text-base-1s font-medium">
PLEASE READ CAREFULLY BEFORE ACCEPT: This action Allow the key
access to your account, the key can be used to send any
transaction on your behalf.
</div>
</div>
</div>
<div className="flex justify-center w-full mt-6">
<button
onClick={onConnect}
className="btn-secondary mr-2 w-full px-4"
>
ACCEPT LOGIN REQUEST
</button>
</div>
</div>
</div>
</div>
);
};

export default AddKeyPage;
3 changes: 2 additions & 1 deletion templates/CreatePage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getXYCoordinates, WebAuthnUtils } from "@/utils/webauthn";
import Field from "@/components/Field";
import { client, parsers } from "@passwordless-id/webauthn";
import { RegistrationEncoded } from "@passwordless-id/webauthn/dist/esm/types";
import { useState } from "react";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import { submitUserOp } from "@/utils/bundler";
import { useRouter } from "next/navigation";
Expand All @@ -18,6 +18,7 @@ const CreatePage = () => {
const wallets = useWalletStore((state) => state.wallets);
const route = useRouter();


const onCreateWallet = async () => {
const randomString = Math.random().toString(36).substring(2, 15);

Expand Down
24 changes: 19 additions & 5 deletions templates/LoginPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"use client";

import PasskeyAccount from "@/account/passkeyAccount";
import Login from "@/components/Login";
import { ethClient } from "@/config";
Expand All @@ -19,8 +20,15 @@ const LoginPage = () => {
const createWallet = useWalletStore((state) => state.onCreateWallet);
const [passkeyName, setPasskeyName] = useState("");
const wallets = useWalletStore((state) => state.wallets);

const [loginLink, setLoginLink] = useState("");
const route = useRouter();

const onLogin = async () => {

}


const restoreWithPasskey = async () => {
const randomString = Math.random().toString(36).substring(2, 15);

Expand All @@ -35,7 +43,9 @@ const LoginPage = () => {
const parsedData = parsers.parseRegistration(regData);

let passkey = getXYCoordinates(parsedData.credential.publicKey);

const loginLink = `${window.origin}/add-key?passkeyId=${parsedData.credential.id}&x=${passkey[0]}&y=${passkey[1]}&device=browser`;

console.log(loginLink);

// TODO: show passkey qr code and wait for pass key is add to wallet

Expand All @@ -61,10 +71,14 @@ const LoginPage = () => {
onChange={(e) => setPasskeyName(e.target.value)}
required
/>

<button className="btn-primary w-full mb-3" onClick={restoreWithPasskey}>
Login wallet
</button>
<div className="flex justify-center w-full mt-6">
<button className="btn-primary mr-2 w-1/2 px-4" onClick={restoreWithPasskey}>
Login
</button>
<button className="btn-primary w-1/2 px-4" onClick={restoreWithPasskey}>
Login with deep link
</button>
</div>
</Login>
);
};
Expand Down