Skip to content

Instantly share code, notes, and snippets.

@ayenisholah
Created December 11, 2024 15:30
Show Gist options
  • Select an option

  • Save ayenisholah/703771008b88ddb6a8116d3874e5f0ef to your computer and use it in GitHub Desktop.

Select an option

Save ayenisholah/703771008b88ddb6a8116d3874e5f0ef to your computer and use it in GitHub Desktop.
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