Created
December 11, 2024 15:30
-
-
Save ayenisholah/703771008b88ddb6a8116d3874e5f0ef to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { FACTORY_ABI, PUBLIC_CLIENT } from "@/constants"; | |
| import { generateNonceFromPubKey } from "@/utils"; | |
| import { NextRequest, NextResponse } from "next/server"; | |
| import { concat, Hex } from "viem"; | |
| import { WebAuthn } from "@/libs/web-authn"; | |
| import { P256Credential, PublicKey, User } from "@/interface"; | |
| export async function POST(request: NextRequest) { | |
| const body = await request.json() | |
| const { credential } = body; | |
| const webAuthnCred = credential as P256Credential; | |
| if (!webAuthnCred?.rawId) { | |
| return NextResponse.json({ error: 'Missing credential rawId', status: 400 }); | |
| } | |
| try { | |
| const pubKey: PublicKey = await WebAuthn.deriveKeysFromRawId(webAuthnCred.rawId); | |
| const nonce = generateNonceFromPubKey(pubKey.x, pubKey.y); | |
| const ownerPublicKey = concat([pubKey.x, pubKey.y]) as Hex; | |
| const address = await PUBLIC_CLIENT.readContract({ | |
| address: process.env.NEXT_PUBLIC_FACTORY_CONTRACT_ADDRESS as Hex, | |
| abi: FACTORY_ABI, | |
| functionName: "getAddress", | |
| args: [[ownerPublicKey], nonce], | |
| }); | |
| const balance = await PUBLIC_CLIENT.getBalance({ address }); | |
| const user: User = { | |
| id: webAuthnCred?.rawId, | |
| pubKey: pubKey, | |
| account: address, | |
| balance: Number(balance) | |
| } | |
| return NextResponse.json(user, { status: 200 }); | |
| } catch (error: any) { | |
| return NextResponse.json({ error: error.message, status: 500 }); | |
| } | |
| } | |
| import { CHAIN, PUBLIC_CLIENT, transport } from '@/constants'; | |
| import { FACTORY_ABI } from '@/constants'; | |
| import { privateKeyToAccount } from 'viem/accounts'; | |
| import { createWalletClient, Hex, concat, parseGwei, parseEther } from 'viem'; | |
| import { generateNonceFromPubKey } from '@/utils'; | |
| import { NextRequest, NextResponse } from 'next/server'; | |
| import { WebAuthn } from "@/libs/web-authn"; | |
| import { CreateCredential, PublicKey, User } from '@/interface'; | |
| import prisma from '@/utils/db'; | |
| export async function POST(request: NextRequest) { | |
| const body = await request.json() | |
| const { credential } = body; | |
| const webAuthnCred = credential as CreateCredential; | |
| if (!webAuthnCred?.rawId) { | |
| return NextResponse.json({ error: 'Missing credential rawId', status: 400 }); | |
| } | |
| try { | |
| const pubKey: PublicKey = await WebAuthn.deriveKeysFromRawId(webAuthnCred.rawId); | |
| const nonce = generateNonceFromPubKey(pubKey.x, pubKey.y); | |
| const ownerPublicKey = concat([pubKey.x, pubKey.y]) as Hex; | |
| const account = privateKeyToAccount(process.env.RELAYER_PRIVATE_KEY as Hex); | |
| const walletClient = createWalletClient({ | |
| account, | |
| chain: CHAIN, | |
| transport, | |
| }); | |
| const addressPromise = PUBLIC_CLIENT.readContract({ | |
| address: process.env.NEXT_PUBLIC_FACTORY_CONTRACT_ADDRESS as Hex, | |
| abi: FACTORY_ABI, | |
| functionName: "getAddress", | |
| args: [[ownerPublicKey], nonce], | |
| }); | |
| await walletClient.writeContract({ | |
| address: process.env.NEXT_PUBLIC_FACTORY_CONTRACT_ADDRESS as Hex, | |
| abi: FACTORY_ABI, | |
| functionName: "createAccount", | |
| args: [[ownerPublicKey], nonce], | |
| value: parseEther("0.0001") | |
| }); | |
| const address = await addressPromise | |
| const user: User = { | |
| id: webAuthnCred.rawId, | |
| pubKey: pubKey, | |
| balance: 0, | |
| account: address | |
| } | |
| const userExists = await prisma.user.findFirst({ | |
| where: { | |
| id: webAuthnCred.rawId | |
| } | |
| }) | |
| if (!userExists) { | |
| await prisma.user.create({ | |
| data: { | |
| id: user.id, | |
| pubKey: JSON.stringify(user.pubKey), | |
| balance: user.balance, | |
| account: user.account | |
| } | |
| }) | |
| } | |
| return NextResponse.json(user, { status: 200 }); | |
| } catch (error: any) { | |
| return NextResponse.json({ error: error.message, status: 500 }); | |
| } | |
| } | |
| const handleTransfer = async () => { | |
| if (!user?.account || !recipientAddress || !amount) { | |
| setTransferError("Please fill in all fields"); | |
| return; | |
| } | |
| setIsTransferring(true); | |
| setTransferError(""); | |
| try { | |
| const nonce = await PUBLIC_CLIENT.getTransactionCount({ | |
| address: user.account, | |
| }); | |
| const value = parseEther(amount); | |
| const transferCallData = encodeFunctionData({ | |
| abi: COINBASE_SMART_WALLET_ABI, | |
| functionName: "execute", | |
| args: [recipientAddress as Hex, value, "0x"], | |
| }); | |
| const userOp: any = { | |
| sender: user.account, | |
| nonce: toHex(nonce), | |
| initCode: "0x" as Hex, | |
| callData: transferCallData, | |
| callGasLimit: toHex(200000), | |
| verificationGasLimit: toHex(200000), | |
| preVerificationGas: toHex(200000), | |
| maxFeePerGas: "0x656703D00", | |
| maxPriorityFeePerGas: "0x13AB6680", | |
| paymasterAndData: "0x" as Hex, | |
| signature: "0x", | |
| }; | |
| const userOpHash = await PUBLIC_CLIENT.readContract({ | |
| address: ENTRYPOINT_ADDRESS, | |
| abi: ENTRYPOINT_ABI, | |
| functionName: "getUserOpHash", | |
| args: [userOp], | |
| }); | |
| const credentials = await WebAuthn.get(userOpHash); | |
| if (!credentials) { | |
| throw new Error("Failed to get WebAuthn credentials"); | |
| } | |
| const signature = await getSignature(user.id, credentials); | |
| userOp.signature = signature; | |
| const bundler = new AlchemyProvider( | |
| "base-sepolia", | |
| NEXT_PUBLIC_ALCHEMY_API_KEY as string | |
| ); | |
| const response = await bundler.send("eth_sendUserOperation", [ | |
| userOp, | |
| ENTRYPOINT_ADDRESS, | |
| ]); | |
| setRecipientAddress(""); | |
| setAmount(""); | |
| } catch (error) { | |
| console.error("Transfer failed:", error); | |
| setTransferError("Transfer failed. Please try again."); | |
| } finally { | |
| setIsTransferring(false); | |
| } | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment