UNPKG

zkapp-cli

Version:

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

300 lines (276 loc) 10.4 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 {JsonProof} from "o1js"; import ZkappWorkerClient from "./ZkappWorkerClient" // 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 [zkappWorkerClient, setZkappWorkerClient] = useState<null | ZkappWorkerClient>(null); 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<JsonProof | 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 () => { console.log("Loading zkApp worker client ..."); const zkappWorkerClient = new ZkappWorkerClient(); setZkappWorkerClient(zkappWorkerClient); await new Promise((resolve) => setTimeout(resolve, 5000)); console.log("done Loading zkApp worker client"); console.log("Setting active instance to Devnet.."); await zkappWorkerClient.setActiveInstanceToDevnet(); await zkappWorkerClient.loadContract(); await zkappWorkerClient.initZkappInstance(zkAppAddress); await zkappWorkerClient.fetchAccount(zkAppAddress); const num = await zkappWorkerClient.getNum(); setContractState(num.toString()); setZkProgramState(num.toString()); // Compile the AddZkProgram console.log("Compiling AddZkProgram"); // ZkProgram cache in the browser is currently not fully supported. await zkappWorkerClient.compileZkProgram(); // Initialize the AddZkProgram with the initial state of the zkapp console.log( "Initialize AddZkProgram with intial contract state of zkapp" ); const initProof = await zkappWorkerClient.initZkProgram(num.toString()); setProof(initProof); // 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 zkappWorkerClient.compileContract(); 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) }); await zkappWorkerClient!.fetchAccount(walletKey); // Execute a transaction locally on the browser let hash; if (proof) { await zkappWorkerClient!.createSettleStateTransaction(proof); // Prove execution of the contract using the proving key console.log("Proving execution of Add.settleState()"); await zkappWorkerClient!.proveSettleStateTransaction(); // Broadcast the transaction to the Mina network const transactionJSON = await zkappWorkerClient!.getTransactionJSON(); console.log("Broadcasting proof of execution to the Mina network"); ({ hash } = await mina.sendTransaction({ transaction: transactionJSON })); } 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) { const updateProof = await zkappWorkerClient!.updateZkProgram( contractState, proof ); setProof(updateProof); setZkProgramState(updateProof.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> </> ); } `;