Skip to content

Commit

Permalink
integrating web3 wallet
Browse files Browse the repository at this point in the history
  • Loading branch information
Pedro Luis Frias Favero authored and Pedro Luis Frias Favero committed Jun 27, 2024
1 parent 94f3aa8 commit dff4d2f
Show file tree
Hide file tree
Showing 4 changed files with 301 additions and 259 deletions.
295 changes: 295 additions & 0 deletions lotto-ui/ui/layouts/web3.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,295 @@
import { Link } from "@nextui-org/link";

import { Head } from "./head";

import { Navbar } from "@/components/navbar";

import GradientBG from "@/components/GradientBG";

import { Field, PublicKey } from 'o1js';
import { useEffect, useState } from 'react';
import styles from '../styles/Home.module.css';
import '../pages/reactCOIServiceWorker';
import ZkappWorkerClient from '../pages/zkappWorkerClient';

let transactionFee = 0.1;
const ZKAPP_ADDRESS = 'B62qpXPvmKDf4SaFJynPsT6DyvuxMS9H1pT4TGonDT26m599m7dS9gP';


export default function Web3Layout({
children,
}: {
children: React.ReactNode;
}) {


const [state, setState] = useState({
zkappWorkerClient: null as null | ZkappWorkerClient,
hasWallet: null as null | boolean,
hasBeenSetup: false,
accountExists: false,
currentNum: null as null | Field,
publicKey: null as null | PublicKey,
zkappPublicKey: null as null | PublicKey,
creatingTransaction: false,
});

const [displayText, setDisplayText] = useState('');
const [transactionlink, setTransactionLink] = useState('');

useEffect(() => {
async function timeout(seconds: number): Promise<void> {
return new Promise<void>((resolve) => {
setTimeout(() => {
resolve();
}, seconds * 1000);
});
}

(async () => {
if (!state.hasBeenSetup) {
setDisplayText('Loading web worker...');
console.log('Loading web worker...');
const zkappWorkerClient = new ZkappWorkerClient();
await timeout(5);

setDisplayText('Done loading web worker');
console.log('Done loading web worker');

await zkappWorkerClient.setActiveInstanceToDevnet();

const mina = (window as any).mina;

if (mina == null) {
setState({ ...state, hasWallet: false });
return;
}

const publicKeyBase58: string = (await mina.requestAccounts())[0];
const publicKey = PublicKey.fromBase58(publicKeyBase58);

console.log(`Using key:${publicKey.toBase58()}`);
setDisplayText(`Using key:${publicKey.toBase58()}`);

setDisplayText('Checking if fee payer account exists...');
console.log('Checking if fee payer account exists...');

const res = await zkappWorkerClient.fetchAccount({
publicKey: publicKey!,
});
const accountExists = res.error == null;

await zkappWorkerClient.loadContract();

console.log('Compiling zkApp...');
setDisplayText('Compiling zkApp...');
await zkappWorkerClient.compileContract();
console.log('zkApp compiled');
setDisplayText('zkApp compiled...');

const zkappPublicKey = PublicKey.fromBase58(ZKAPP_ADDRESS);

await zkappWorkerClient.initZkappInstance(zkappPublicKey);

console.log('Getting zkApp state...');
setDisplayText('Getting zkApp state...');
await zkappWorkerClient.fetchAccount({ publicKey: zkappPublicKey });
const currentNum = await zkappWorkerClient.getNum();
console.log(`Current state in zkApp: ${currentNum.toString()}`);
setDisplayText('');

setState({
...state,
zkappWorkerClient,
hasWallet: true,
hasBeenSetup: true,
publicKey,
zkappPublicKey,
accountExists,
currentNum,
});
}
})();
}, []);

// -------------------------------------------------------
// Wait for account to exist, if it didn't

useEffect(() => {
(async () => {
if (state.hasBeenSetup && !state.accountExists) {
for (;;) {
setDisplayText('Checking if fee payer account exists...');
console.log('Checking if fee payer account exists...');
const res = await state.zkappWorkerClient!.fetchAccount({
publicKey: state.publicKey!,
});
const accountExists = res.error == null;
if (accountExists) {
break;
}
await new Promise((resolve) => setTimeout(resolve, 5000));
}
setState({ ...state, accountExists: true });
}
})();
}, [state.hasBeenSetup]);


// -------------------------------------------------------
// Send a transaction

const onSendTransaction = async () => {
setState({ ...state, creatingTransaction: true });

setDisplayText('Creating a transaction...');
console.log('Creating a transaction...');

await state.zkappWorkerClient!.fetchAccount({
publicKey: state.publicKey!,
});

await state.zkappWorkerClient!.createUpdateTransaction();

setDisplayText('Creating proof...');
console.log('Creating proof...');
await state.zkappWorkerClient!.proveUpdateTransaction();

console.log('Requesting send transaction...');
setDisplayText('Requesting send transaction...');
const transactionJSON = await state.zkappWorkerClient!.getTransactionJSON();

setDisplayText('Getting transaction JSON...');
console.log('Getting transaction JSON...');
const { hash } = await (window as any).mina.sendTransaction({
transaction: transactionJSON,
feePayer: {
fee: transactionFee,
memo: '',
},
});

const transactionLink = `https://minascan.io/devnet/tx/${hash}`;
console.log(`View transaction at ${transactionLink}`);

setTransactionLink(transactionLink);
setDisplayText(transactionLink);

setState({ ...state, creatingTransaction: false });
};

// -------------------------------------------------------
// Refresh the current state

const onRefreshCurrentNum = async () => {
console.log('Getting zkApp state...');
setDisplayText('Getting zkApp state...');

await state.zkappWorkerClient!.fetchAccount({
publicKey: state.zkappPublicKey!,
});
const currentNum = await state.zkappWorkerClient!.getNum();
setState({ ...state, currentNum });
console.log(`Current state in zkApp: ${currentNum.toString()}`);
setDisplayText('');
};

// -------------------------------------------------------
// Create UI elements

let hasWallet;
if (state.hasWallet != null && !state.hasWallet) {
const auroLink = 'https://www.aurowallet.com/';
const auroLinkElem = (
<a href={auroLink} target="_blank" rel="noreferrer">
Install Auro wallet here
</a>
);
hasWallet = <div>Could not find a wallet. {auroLinkElem}</div>;
}

const stepDisplay = transactionlink ? (
<a
href={transactionlink}
target="_blank"
rel="noreferrer"
style={{ textDecoration: 'underline' }}
>
View transaction
</a>
) : (
displayText
);

let setup = (
<div
className={styles.start}
style={{ fontWeight: 'bold', fontSize: '1.5rem', paddingBottom: '5rem' }}
>
{stepDisplay}
{hasWallet}
</div>
);

let accountDoesNotExist;
if (state.hasBeenSetup && !state.accountExists) {
const faucetLink =
'https://faucet.minaprotocol.com/?address=' + state.publicKey!.toBase58();
accountDoesNotExist = (
<div>
<span style={{ paddingRight: '1rem' }}>Account does not exist.</span>
<a href={faucetLink} target="_blank" rel="noreferrer">
Visit the faucet to fund this fee payer account
</a>
</div>
);
}

let mainContent;
if (state.hasBeenSetup && state.accountExists) {
mainContent = (
<div style={{ justifyContent: 'center', alignItems: 'center' }}>
<div className={styles.center} style={{ padding: 0 }}>
Current state in zkApp: {state.currentNum!.toString()}{' '}
</div>
<button
className={styles.card}
onClick={onSendTransaction}
disabled={state.creatingTransaction}
>
Send Transaction
</button>
<button className={styles.card} onClick={onRefreshCurrentNum}>
Get Latest State
</button>
</div>
);
}



return (
<GradientBG>
<div className="relative flex flex-col h-screen">
<Head />
<Navbar />
<main className="container mx-auto max-w-7xl px-6 flex-grow pt-16">
{setup}
{accountDoesNotExist}
{children}
</main>
<footer className="w-full flex items-center justify-center py-3">
<Link
isExternal
className="flex items-center gap-1 text-current"
href="https://minaprotocol.com/zkapps"
title="minaprotocol.com homepage"
>
<span className="text-default-600">Powered by</span>
<p className="text-primary">Mina</p>
</Link>
</footer>
</div>
</GradientBG>
);
}
6 changes: 3 additions & 3 deletions lotto-ui/ui/pages/admin/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import DefaultLayout from "@/layouts/default";
import { title } from "@/components/primitives";
import React from "react";
import { Card, CardHeader, Spacer, Input, Button } from "@nextui-org/react";
import dynamic from "next/dynamic";
import Web3Layout from "@/layouts/web3";

// Cargar Countdown dinámicamente para evitar errores de hidratación
const Countdown = dynamic(() => import("../../components/Countdown"), { ssr: false });
Expand All @@ -20,7 +20,7 @@ const getNextMonday = (): string => {
export default function AdminPage() {
const targetDate = getNextMonday();
return (
<DefaultLayout>
<Web3Layout>
<h1 className={`${title()} text-green-500 pb-2`}>
Admin dashboard
</h1>
Expand Down Expand Up @@ -109,6 +109,6 @@ export default function AdminPage() {
End Game Week
</Button>
</div>
</DefaultLayout>
</Web3Layout>
);
}
5 changes: 3 additions & 2 deletions lotto-ui/ui/pages/dashboard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { title } from "@/components/primitives";
import React from "react";
import { Card, CardHeader, Spacer, Input, Button, Divider } from "@nextui-org/react";
import dynamic from "next/dynamic";
import Web3Layout from "@/layouts/web3";

// Cargar Countdown dinámicamente para evitar errores de hidratación
const Countdown = dynamic(() => import("../../components/Countdown"), { ssr: false });
Expand All @@ -19,7 +20,7 @@ const getNextMonday = (): string => {
export default function DashboardPage() {
const targetDate = getNextMonday();
return (
<DefaultLayout>
<Web3Layout>
<h1 className={`${title()} text-green-500 pb-2`}>
Dashboard
</h1>
Expand Down Expand Up @@ -128,6 +129,6 @@ export default function DashboardPage() {
Claim
</Button>
</div>
</DefaultLayout>
</Web3Layout>
);
}
Loading

0 comments on commit dff4d2f

Please sign in to comment.