UNPKG

zkapp-cli

Version:

CLI to create zkApps (zero-knowledge apps) for Mina Protocol

276 lines (256 loc) 9.8 kB
export default `'use client'; import Head from 'next/head'; import Image from 'next/image'; import {useCallback, useEffect, useRef, useState} from 'react'; import GradientBG from '../components/GradientBG.js'; import styles from '../styles/Home.module.css'; import heroMinaLogo from '../public/assets/hero-mina-logo.svg'; import arrowRightSmall from '../public/assets/arrow-right-small.svg'; import {fetchAccount, Mina, PublicKey, Field, Proof} from "o1js"; import { Add, AddZkProgram } from "../../contracts"; // We've already deployed the Add contract on testnet at this address // https://minascan.io/devnet/account/B62qnfpb1Wz7DrW7279B8nR8m4yY6wGJz4dnbAdkzfeUkpyp8aB9VCp const zkAppAddress = "B62qnfpb1Wz7DrW7279B8nR8m4yY6wGJz4dnbAdkzfeUkpyp8aB9VCp"; export default function Home() { const zkApp = useRef<Add>(new Add(PublicKey.fromBase58(zkAppAddress))); const [transactionLink, setTransactionLink] = useState<string | null>(null); const [contractState, setContractState] = useState<string | null>(null); const [zkProgramState, setZkProgramState] = useState<string | null>(null); const [proof, setProof] = useState<Proof<Field, Field> | null>(null); const [error, setError] = useState<string | null>(null); const [loading, setLoading] = useState<boolean>(true); // fetch the zkapp state when the page loads useEffect(() => { (async () => { Mina.setActiveInstance(Mina.Network('https://api.minascan.io/node/devnet/v1/graphql')); await fetchAccount({publicKey: zkAppAddress}); const num = zkApp.current.num.get(); setContractState(num.toString()); setZkProgramState(num.toString()); // Compile the AddZkProgram console.log("Compiling AddZkProgram"); await AddZkProgram.compile(); // Initialize the AddZkProgram with the initial state of the zkapp console.log("Initialize AddZkProgram with intial contract state of zkapp"); const init = await AddZkProgram.init(num); setProof(init.proof); // Compile the contract so that o1js has the proving key required to execute contract calls console.log("Compiling Add contract to generate proving and verification keys"); await Add.compile(); setLoading(false); })(); }, []); const updateZkApp = useCallback(async () => { setTransactionLink(null); setLoading(true); try { // Retrieve Mina provider injected by browser extension wallet const mina = (window as any).mina; const walletKey: string = (await mina.requestAccounts())[0]; console.log("Connected wallet address: " + walletKey); await fetchAccount({publicKey: PublicKey.fromBase58(walletKey)}); // Execute a transaction locally on the browser let hash; if (proof) { const transaction = await Mina.transaction(async () => { console.log("Executing Add.settleState() locally"); await zkApp.current.settleState(proof); }); // Prove execution of the contract using the proving key console.log("Proving execution of Add.settleState()"); await transaction.prove(); // Broadcast the transaction to the Mina network console.log("Broadcasting proof of execution to the Mina network"); ({ hash } = await mina.sendTransaction({ transaction: transaction.toJSON() })); } else { throw Error("Proof passed to Add.settleState is null"); } // display the link to the transaction const transactionLink = "https://minascan.io/devnet/tx/" + hash; setTransactionLink(transactionLink); } catch (e: any) { console.error(e.message); let errorMessage = ""; if (e.message.includes("Cannot read properties of undefined (reading 'requestAccounts')")) { errorMessage = "Is Auro installed?"; } else if (e.message.includes("Please create or restore wallet first.")) { errorMessage = "Have you created a wallet?"; } else if (e.message.includes("User rejected the request.")) { errorMessage = "Did you grant the app permission to connect?"; } else { errorMessage = "An unknown error occurred."; } setError(errorMessage); } finally { setLoading(false); } }, [proof]); const updateZkProgram = useCallback(async () => { setLoading(true); if (contractState && proof) { // Call the AddZkProgram update method console.log("Calling AddZkProgram.update"); const update = await AddZkProgram.update(Field(contractState), proof); setProof(update.proof); setZkProgramState(update.proof.publicOutput.toString()) } else { throw Error("Proof and or ContractState passed to AddZkProgram.update is null"); } setLoading(false); }, [proof]); return ( <> <Head> <title>Mina zkApp UI</title> <meta name="description" content="built with o1js"/> <link rel="icon" href="/assets/favicon.ico"/> </Head> <GradientBG> <main className={styles.main}> <div className={styles.center}> <a href="https://minaprotocol.com/" target="_blank" rel="noopener noreferrer" > <Image className={styles.logo} src={heroMinaLogo} alt="Mina Logo" width="191" height="174" priority /> </a> <p className={styles.tagline}> built with <code className={styles.code}> o1js</code> </p> </div> <p className={styles.start}> Get started by editing <code className={styles.code}> app/page.tsx</code> </p> <div className={styles.stateContainer}> <div className={styles.state}> <div> <div>Contract State: <span className={styles.bold}>{contractState}</span></div> {error ? ( <span className={styles.error}>Error: {error}</span> ) : (loading ? <div>Loading...</div> : (transactionLink ? <a href={transactionLink} className={styles.bold} target="_blank" rel="noopener noreferrer"> View Transaction on MinaScan </a> : <button onClick={updateZkApp} className={styles.button}>Add.settleState()</button>))} </div> </div> <div className={styles.state}> <div> <div> ZkProgram State:{" "} <span className={styles.bold}>{zkProgramState}</span> </div> {error ? ( <span className={styles.error}>Error: {error}</span> ) : loading ? ( <div>Loading...</div> ) : ( <button onClick={updateZkProgram} className={styles.button}> AddZkProgram.update() </button> )} </div> </div> </div> <div className={styles.grid}> <a href="https://docs.minaprotocol.com/zkapps" className={styles.card} target="_blank" rel="noopener noreferrer" > <h2> <span>DOCS</span> <div> <Image src={arrowRightSmall} alt="Mina Logo" width={16} height={16} priority /> </div> </h2> <p>Explore zkApps, how to build one, and in-depth references</p> </a> <a href="https://docs.minaprotocol.com/zkapps/tutorials/hello-world" className={styles.card} target="_blank" rel="noopener noreferrer" > <h2> <span>TUTORIALS</span> <div> <Image src={arrowRightSmall} alt="Mina Logo" width={16} height={16} priority /> </div> </h2> <p>Learn with step-by-step o1js tutorials</p> </a> <a href="https://discord.gg/minaprotocol" className={styles.card} target="_blank" rel="noopener noreferrer" > <h2> <span>QUESTIONS</span> <div> <Image src={arrowRightSmall} alt="Mina Logo" width={16} height={16} priority /> </div> </h2> <p>Ask questions on our Discord server</p> </a> <a href="https://docs.minaprotocol.com/zkapps/how-to-deploy-a-zkapp" className={styles.card} target="_blank" rel="noopener noreferrer" > <h2> <span>DEPLOY</span> <div> <Image src={arrowRightSmall} alt="Mina Logo" width={16} height={16} priority /> </div> </h2> <p>Deploy a zkApp to Testnet</p> </a> </div> </main> </GradientBG> </> ); } `;