From 3cfa82aa6d41c4bb74c70ab7c86ae134ebec9b12 Mon Sep 17 00:00:00 2001 From: hattyhattington17 <181872047+hattyhattington17@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:26:31 -0700 Subject: [PATCH 1/3] add interactivity to nextjs starter project --- src/lib/ui/next/customNextPage.js | 108 +++++++++++++++++++++---- src/lib/ui/next/styles/Home.module.css | 31 +++++++ 2 files changed, 122 insertions(+), 17 deletions(-) diff --git a/src/lib/ui/next/customNextPage.js b/src/lib/ui/next/customNextPage.js index c5ef9dea..52e02e81 100644 --- a/src/lib/ui/next/customNextPage.js +++ b/src/lib/ui/next/customNextPage.js @@ -1,38 +1,96 @@ export default `'use client'; import Head from 'next/head'; import Image from 'next/image'; -import { useEffect } from 'react'; +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 './reactCOIServiceWorker'; +import {fetchAccount, Mina, PublicKey} from "o1js"; +import {Add} from "../../contracts"; + + +// We've already deployed the Add contract on testnet at this address +// https://minascan.io/devnet/account/B62qnTDEeYtBHBePA4yhCt4TCgDtA4L2CGvK7PirbJyX4pKH8bmtWe5 +const zkAppAddress = "B62qnTDEeYtBHBePA4yhCt4TCgDtA4L2CGvK7PirbJyX4pKH8bmtWe5"; export default function Home() { + const zkApp = useRef(new Add(PublicKey.fromBase58(zkAppAddress))); + + const [transactionLink, setTransactionLink] = useState(null); + const [contractState, setContractState] = useState(null); + const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); + + // fetch the zkapp state when the page loads useEffect(() => { (async () => { - const { Mina, PublicKey } = await import('o1js'); - const { Add } = await import('../../contracts/build/src/'); + 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()); + 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)}); - // Update this to use the address (public key) for your zkApp account. - // To try it out, you can try this address for an example "Add" smart contract that we've deployed to - // Testnet B62qnTDEeYtBHBePA4yhCt4TCgDtA4L2CGvK7PirbJyX4pKH8bmtWe5. - const zkAppAddress = ''; - // This should be removed once the zkAppAddress is updated. - if (!zkAppAddress) { - console.error( - 'The following error is caused because the zkAppAddress has an empty string as the public key. Update the zkAppAddress with the public key for your zkApp account, or try this address for an example "Add" smart contract that we deployed to Testnet: B62qnTDEeYtBHBePA4yhCt4TCgDtA4L2CGvK7PirbJyX4pKH8bmtWe5' - ); + // 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(); + + // Execute a transaction locally on the browser + const transaction = await Mina.transaction(async () => { + console.log("Executing Add.update() locally"); + await zkApp.current.update(); + }); + + // Prove execution of the contract using the proving key + console.log("Proving execution of Add.update()"); + await transaction.prove(); + + // Broadcast the transaction to the Mina network + console.log("Broadcasting proof of execution to the Mina network"); + const {hash} = await mina.sendTransaction({transaction: transaction.toJSON()}); + + // 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 to your wallet?"; + } else { + errorMessage = "An unknown error occurred."; } - //const zkApp = new Add(PublicKey.fromBase58(zkAppAddress)) - })(); + setError(errorMessage); + } finally { + setLoading(false); + } }, []); return ( <> Mina zkApp UI - - + +
@@ -60,6 +118,22 @@ export default function Home() { Get started by editing app/page.tsx

+
+ {error ? ( + Error: {error} + ) : ( +
+
Contract State: {contractState}
+ {loading ? +
Loading...
: + (transactionLink ? + + View Transaction on MinaScan + : + )} +
+ )} +
); } -`; +`; \ No newline at end of file diff --git a/src/lib/ui/next/styles/Home.module.css b/src/lib/ui/next/styles/Home.module.css index ca0dba2b..88d4e983 100644 --- a/src/lib/ui/next/styles/Home.module.css +++ b/src/lib/ui/next/styles/Home.module.css @@ -75,6 +75,37 @@ justify-items: center; } +.state button { + display: block; + margin: .5rem auto 1rem; + background-color: rgb(255, 255, 255); + border: 1px solid #2d2d2d; + padding: .25rem .5rem; + cursor: pointer; +} +.state button:hover { + background-color: lightgray; +} + +.state { + padding: 0.8rem 1rem; + gap: 0.5rem; + margin: 1rem; + background-color: rgb(255, 255, 255); + border: 1px solid #2d2d2d; + width: 14rem; + height: 6rem; + text-align: center; +} + +.state .error { + color: red; +} + +.state .bold { + font-family: var(--font-monument-bold); +} + .card { padding: 0.8rem 1rem; gap: 0.5rem; From 7b6e280aebb2f7747e3cf9e0a163de82da5c568e Mon Sep 17 00:00:00 2001 From: hattyhattington17 <181872047+hattyhattington17@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:31:45 -0700 Subject: [PATCH 2/3] update changelog --- CHANGELOG.md | 6 ++++++ package.json | 2 +- src/lib/ui/next/customNextPage.js | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd752847..a94da867 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,12 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## Unreleased +## [0.22.2](https://github.com/o1-labs/zkapp-cli/compare/v0.22.1...v0.22.2) - 2024-12-1 + +### Changed + +- Add interactivity to Next.js starter app ui. [#709](https://github.com/o1-labs/zkapp-cli/pull/709) + ## [0.22.1](https://github.com/o1-labs/zkapp-cli/compare/v0.22.0...v0.22.1) - 2024-11-17 ### Changed diff --git a/package.json b/package.json index 428e03d0..c8501bd6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zkapp-cli", - "version": "0.22.1", + "version": "0.22.2", "description": "CLI to create zkApps (zero-knowledge apps) for Mina Protocol", "homepage": "https://github.com/o1-labs/zkapp-cli/", "repository": { diff --git a/src/lib/ui/next/customNextPage.js b/src/lib/ui/next/customNextPage.js index 52e02e81..3b387ee7 100644 --- a/src/lib/ui/next/customNextPage.js +++ b/src/lib/ui/next/customNextPage.js @@ -221,4 +221,4 @@ export default function Home() { ); } -`; \ No newline at end of file +`; From 05fb32ed444968317585d387818d47891bceddce Mon Sep 17 00:00:00 2001 From: hattyhattington17 <181872047+hattyhattington17@users.noreply.github.com> Date: Thu, 5 Dec 2024 12:10:45 -0700 Subject: [PATCH 3/3] move state display out of error ternary, compile contract on page load --- src/lib/ui/next/customNextPage.js | 39 ++++++++++++++++--------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/lib/ui/next/customNextPage.js b/src/lib/ui/next/customNextPage.js index 3b387ee7..0881fa0a 100644 --- a/src/lib/ui/next/customNextPage.js +++ b/src/lib/ui/next/customNextPage.js @@ -15,6 +15,8 @@ import {Add} from "../../contracts"; // https://minascan.io/devnet/account/B62qnTDEeYtBHBePA4yhCt4TCgDtA4L2CGvK7PirbJyX4pKH8bmtWe5 const zkAppAddress = "B62qnTDEeYtBHBePA4yhCt4TCgDtA4L2CGvK7PirbJyX4pKH8bmtWe5"; +import './reactCOIServiceWorker'; + export default function Home() { const zkApp = useRef(new Add(PublicKey.fromBase58(zkAppAddress))); @@ -30,6 +32,11 @@ export default function Home() { await fetchAccount({publicKey: zkAppAddress}); const num = zkApp.current.num.get(); setContractState(num.toString()); + + // 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); })(); }, []); @@ -45,10 +52,6 @@ export default function Home() { console.log("Connected wallet address: " + walletKey); await fetchAccount({publicKey: PublicKey.fromBase58(walletKey)}); - // 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(); - // Execute a transaction locally on the browser const transaction = await Mina.transaction(async () => { console.log("Executing Add.update() locally"); @@ -75,7 +78,7 @@ export default function Home() { } 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 to your wallet?"; + errorMessage = "Did you grant the app permission to connect?"; } else { errorMessage = "An unknown error occurred."; } @@ -119,20 +122,18 @@ export default function Home() { app/page.tsx

- {error ? ( - Error: {error} - ) : ( - - )} +
+
Contract State: {contractState}
+ {error ? ( + Error: {error} + ) : (loading ? +
Loading...
: + (transactionLink ? + + View Transaction on MinaScan + : + ))} +