Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add interactivity to nextjs starter project #709

Merged
merged 3 commits into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
109 changes: 92 additions & 17 deletions src/lib/ui/next/customNextPage.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,99 @@
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";

import './reactCOIServiceWorker';

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 [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState<boolean>(true);

// fetch the zkapp state when the page loads
useEffect(() => {
(async () => {
const { Mina, PublicKey } = await import('o1js');
const { Add } = await import('../../contracts/build/src/');

// 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'
);
}
//const zkApp = new Add(PublicKey.fromBase58(zkAppAddress))
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());

// 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
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()});
boray marked this conversation as resolved.
Show resolved Hide resolved

// display the link to the transaction
const transactionLink = "https://minascan.io/devnet/tx/" + hash;
setTransactionLink(transactionLink);
} catch (e: any) {
hattyhattington17 marked this conversation as resolved.
Show resolved Hide resolved
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);
}
}, []);

return (
<>
<Head>
<title>Mina zkApp UI</title>
<meta name="description" content="built with o1js" />
<link rel="icon" href="/assets/favicon.ico" />
<meta name="description" content="built with o1js"/>
<link rel="icon" href="/assets/favicon.ico"/>
</Head>
<GradientBG>
<main className={styles.main}>
Expand Down Expand Up @@ -60,6 +121,20 @@ export default function Home() {
Get started by editing
<code className={styles.code}> app/page.tsx</code>
</p>
<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}>Call Add.update()</button>))}
</div>
</div>
<div className={styles.grid}>
<a
href="https://docs.minaprotocol.com/zkapps"
Expand Down
31 changes: 31 additions & 0 deletions src/lib/ui/next/styles/Home.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading