From 242359d1ffa96d0c1b911df9659621d6d23eacec Mon Sep 17 00:00:00 2001 From: Shiv Bhonde Date: Wed, 6 Nov 2024 02:56:23 +0700 Subject: [PATCH] send transction from backend --- packages/hardhat/contracts/YourContract.sol | 29 ++-- packages/nextjs/app/api/mint/route.ts | 143 ++++++++++++++++++ packages/nextjs/app/zpass/page.tsx | 74 +++++---- .../nextjs/contracts/deployedContracts.ts | 4 +- 4 files changed, 200 insertions(+), 50 deletions(-) create mode 100644 packages/nextjs/app/api/mint/route.ts diff --git a/packages/hardhat/contracts/YourContract.sol b/packages/hardhat/contracts/YourContract.sol index 5af582c..a9933a5 100644 --- a/packages/hardhat/contracts/YourContract.sol +++ b/packages/hardhat/contracts/YourContract.sol @@ -90,41 +90,34 @@ contract YourContract is ERC721, Groth16Verifier, Poseidon { // Verify beauty input[0] = attrs.beauty; - console.log(this.hash(input)); - // require(this.hash(input) == pubSignals[0], "Invalid beauty value"); + require(this.hash(input) == pubSignals[0], "Invalid beauty value"); // Verify biome input[0] = attrs.biome; - console.log(this.hash(input)); - // require(this.hash(input) == pubSignals[1], "Invalid biome value"); - // + require(this.hash(input) == pubSignals[1], "Invalid biome value"); + // Verify intelligence input[0] = attrs.intelligence; - console.log(this.hash(input)); - // require( - // this.hash(input) == pubSignals[2], - // "Invalid intelligence value" - // ); + require( + this.hash(input) == pubSignals[3], + "Invalid intelligence value" + ); // Verify jump input[0] = attrs.jump; - console.log(this.hash(input)); - // require(this.hash(input) == pubSignals[3], "Invalid jump value"); + require(this.hash(input) == pubSignals[4], "Invalid jump value"); // Verify owner input[0] = attrs.owner; - console.log(this.hash(input)); - // require(this.hash(input) == pubSignals[5], "Invalid owner value"); + require(this.hash(input) == pubSignals[6], "Invalid owner value"); // Verify rarity input[0] = attrs.rarity; - console.log(this.hash(input)); - // require(this.hash(input) == pubSignals[6], "Invalid rarity value"); + require(this.hash(input) == pubSignals[7], "Invalid rarity value"); // Verify speed input[0] = attrs.speed; - console.log(this.hash(input)); - // require(this.hash(input) == pubSignals[7], "Invalid speed value"); + require(this.hash(input) == pubSignals[8], "Invalid speed value"); return true; } diff --git a/packages/nextjs/app/api/mint/route.ts b/packages/nextjs/app/api/mint/route.ts new file mode 100644 index 0000000..3c4c8d9 --- /dev/null +++ b/packages/nextjs/app/api/mint/route.ts @@ -0,0 +1,143 @@ +// app/api/mint/route.ts +import { NextResponse } from "next/server"; +import { createWalletClient, fallback, http, verifyMessage } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import deployedContracts from "~~/contracts/deployedContracts"; +import scaffoldConfig from "~~/scaffold.config"; +import { getAlchemyHttpUrl, getParsedError } from "~~/utils/scaffold-eth"; + +const wallet_private_key = (process.env.WALLET_PRIVATE_KEY || + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80") as `0x${string}`; + +const { targetNetworks } = scaffoldConfig; + +const mainNetwork = targetNetworks[0]; +const alchemyHttpUrl = getAlchemyHttpUrl(mainNetwork.id); +const rpcFallbacks = alchemyHttpUrl ? [http(alchemyHttpUrl), http()] : [http()]; + +const walletClient = createWalletClient({ + chain: mainNetwork, + transport: fallback(rpcFallbacks), + account: privateKeyToAccount(wallet_private_key), +}); + +type ProofData = { + pi_a: string[]; + pi_b: string[][]; // Array of arrays for pi_b + pi_c: string[]; + pubSignals: string[]; +}; + +type FrogStats = { + beauty: string; + biome: string; + intelligence: string; + jump: string; + speed: string; + rarity: string; + owner: string; + name: string; + description: string; +}; + +type MintRequestBody = { + proof: ProofData; + frogStats: FrogStats; + signature: string; + address: string; +}; + +// Utility function to convert string arrays to BigInt arrays +const convertToBigIntArray = (arr: string[]): bigint[] => { + return arr.map(str => BigInt(str)); +}; + +// Utility function to convert nested string arrays to nested BigInt arrays +const convertToBigIntNestedArray = (arr: string[][]): bigint[][] => { + return arr.map(subArr => subArr.map(str => BigInt(str))); +}; + +// Utility function to convert frog stats to BigInt +const convertFrogStatsToBigInt = (stats: Omit) => { + return { + beauty: BigInt(stats.beauty), + biome: BigInt(stats.biome), + intelligence: BigInt(stats.intelligence), + jump: BigInt(stats.jump), + speed: BigInt(stats.speed), + rarity: BigInt(stats.rarity), + owner: BigInt(stats.owner), + name: stats.name, // Keep as string + }; +}; + +// Utility function to convert proof data to BigInt +const convertProofToBigInt = (proof: ProofData) => { + return { + pi_a: convertToBigIntArray(proof.pi_a), + pi_b: convertToBigIntNestedArray(proof.pi_b), // Using nested array conversion for pi_b + pi_c: convertToBigIntArray(proof.pi_c), + pubSignals: convertToBigIntArray(proof.pubSignals), + }; +}; + +export async function POST(req: Request) { + try { + const body: MintRequestBody = await req.json(); + + // Convert string values to BigInt + const convertedProof = convertProofToBigInt(body.proof); + const { description, ...actualStats } = body.frogStats; + const convertedFrogStats = convertFrogStatsToBigInt(actualStats); + + // Verify signature + const isValidSignature = await verifyMessage({ + message: `I own ${body.frogStats.name}`, + signature: body.signature as `0x${string}`, + address: body.address as `0x${string}`, + }); + + if (!isValidSignature) { + return NextResponse.json({ error: "Invalid signature" }, { status: 400 }); + } + + console.log("The description values are: ", description); + + const { address: contractAddress, abi: contractAbi } = deployedContracts[mainNetwork.id].YourContract; + const hash = await walletClient.writeContract({ + address: contractAddress, + abi: contractAbi, + functionName: "mintFrog", + args: [ + { + _pA: convertedProof.pi_a as any, + _pB: convertedProof.pi_b as any, + _pC: convertedProof.pi_c as any, + _pubSignals: convertedProof.pubSignals as any, + }, + { + beauty: convertedFrogStats.beauty, + biome: convertedFrogStats.biome, + intelligence: convertedFrogStats.intelligence, + jump: convertedFrogStats.jump, + speed: convertedFrogStats.speed, + rarity: convertedFrogStats.rarity, + owner: convertedFrogStats.owner, + }, + ], + }); + + return NextResponse.json( + { + success: true, + message: "NFT minted successfully", + txHash: hash, + }, + { status: 200 }, + ); + } catch (error) { + const parsedErrorMessage = getParsedError(error); + console.error(error); + return NextResponse.json({ error: parsedErrorMessage }, { status: 500 }); + } +} diff --git a/packages/nextjs/app/zpass/page.tsx b/packages/nextjs/app/zpass/page.tsx index c75101e..eaaf2f6 100644 --- a/packages/nextjs/app/zpass/page.tsx +++ b/packages/nextjs/app/zpass/page.tsx @@ -7,9 +7,9 @@ import { gpcPreVerify } from "@pcd/gpc"; import { ProtoPODGPC } from "@pcd/gpcircuits"; import { POD, PODEntries } from "@pcd/pod"; import { PartialDeep } from "type-fest"; -import { useAccount } from "wagmi"; -import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth"; -import { notification } from "~~/utils/scaffold-eth"; +import { useAccount, useSignMessage } from "wagmi"; +import { getParsedError, notification } from "~~/utils/scaffold-eth"; +import { replacer } from "~~/utils/scaffold-eth/common"; export interface PODData { entries: PODEntries; @@ -75,7 +75,7 @@ const ZuAuth = () => { const [z, setZ] = useState(null); const [isLoading, setIsLoading] = useState(false); - const { writeContractAsync: writeYourContractAsync } = useScaffoldWriteContract("YourContract"); + const { signMessageAsync } = useSignMessage(); const handleAuth = async () => { try { @@ -137,16 +137,10 @@ const ZuAuth = () => { if (result.success) { const boundConfig = result.boundConfig; const revealedClaims = result.revealedClaims; - console.log("The revealed claims", revealedClaims); - console.log("The proof is:", result.proof); - const circuit = gpcPreVerify(boundConfig, revealedClaims); const pubSignals = ProtoPODGPC.makePublicSignals(circuit.circuitPublicInputs, circuit.circuitOutputs); - console.log("The public signals", pubSignals); const frogStats = revealedClaims.pods.FROGCRYPTO?.entries; - console.log("The reveleadClaims", revealedClaims); - console.log("The frog stats", frogStats); const frogName = frogStats?.name.value; const beauty = frogStats?.beauty.value as any as bigint; @@ -156,36 +150,56 @@ const ZuAuth = () => { const speed = frogStats?.speed.value as any as bigint; const rarity = frogStats?.rarity.value as any as bigint; const owner = frogStats?.owner.value as any as bigint; + const description = frogStats?.description.value as any as string; notification.info("Minting your Frog NFT..."); + const signature = await signMessageAsync({ + message: `I own ${frogName}`, + }); - const mintResult = await writeYourContractAsync({ - functionName: "mintFrog", - args: [ - { - _pA: result.proof.pi_a.slice(0, -1), - _pB: result.proof.pi_b.slice(0, -1), - _pC: result.proof.pi_c.slice(0, -1), - _pubSignals: pubSignals as any, - }, + // Send data to backend + const response = await fetch("/api/mint", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify( { - beauty, - biome, - intelligence, - jump, - speed, - rarity, - owner, + proof: { + pi_a: result.proof.pi_a.slice(0, -1), + pi_b: result.proof.pi_b.slice(0, -1), + pi_c: result.proof.pi_c.slice(0, -1), + pubSignals: pubSignals, + }, + frogStats: { + beauty: beauty.toString(), + biome: biome.toString(), + intelligence: intelligence.toString(), + jump: jump.toString(), + speed: speed.toString(), + rarity: rarity.toString(), + owner: owner.toString(), + name: frogName, + description, + }, + signature, + address: connectedAddress, }, - ], + replacer, + ), }); + const data = await response.json(); - console.log("Mint transaction:", mintResult); + if (!response.ok) { + throw new Error(data.error); + } + + console.log("The data is", data); notification.success(`Successfully minted Frog NFT: ${frogName}`); } } catch (e) { - console.log("error", e); - notification.error("Failed to mint NFT"); + const errorMessage = getParsedError(e); + notification.error(errorMessage); } finally { setIsLoading(false); } diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index efc8699..182bb4b 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -7,7 +7,7 @@ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; const deployedContracts = { 31337: { Poseidon: { - address: "0x5FC8d32690cc91D4c39d9d3abcBD16989F875707", + address: "0x5FbDB2315678afecb367f032d93F642f64180aa3", abi: [ { inputs: [ @@ -32,7 +32,7 @@ const deployedContracts = { inheritedFunctions: {}, }, YourContract: { - address: "0x0165878A594ca255338adfa4d48449f69242Eb8F", + address: "0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512", abi: [ { inputs: [],