Skip to content
Open
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
Empty file added frontend/README
Empty file.
10 changes: 10 additions & 0 deletions frontend/components/content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

import React from 'react';

export default function Content({ children }: { children: React.ReactNode }) {
return (
<div className="container px-4 mx-auto">
<main className="pt-4">{children}</main>
</div>
);
}
10 changes: 10 additions & 0 deletions frontend/components/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

import React from 'react';

export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div className="container px-4 mx-auto">
<main className="pt-4">{children}</main>
</div>
);
}
2 changes: 1 addition & 1 deletion frontend/contracts/contract-address.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"Token": "0x5FbDB2315678afecb367f032d93F642f64180aa3"
"TutorialToken": "0x2279B7A0a67DB372996a5FaB50D91eAA73d2eBe6"
}
19 changes: 19 additions & 0 deletions frontend/hooks/useAllowList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { db } from '../lib/firebase';
import { doc, setDoc } from "firebase/firestore";

type useAllowListType = () => {
sendRequestAllowList: (address: string, mailaddress: string) => Promise<void>;
}

export const useAllowList: useAllowListType = () => {
async function sendRequestAllowList(address: string, mailaddress: string): Promise<void> {
await setDoc(doc(db, "allowList", address), {
address,
mailaddress,
});
}

return {
sendRequestAllowList,
}
};
2 changes: 1 addition & 1 deletion frontend/hooks/useConnectWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const useConnectWallet = () => {
await provider.send('eth_requestAccounts', []);
const signer = provider.getSigner();
const userAddress = await signer.getAddress();
console.log(userAddress);
return userAddress;
} catch (error) {
console.log('Error connecting to metamask', error);
}
Expand Down
45 changes: 45 additions & 0 deletions frontend/hooks/useRequestAllowListFormModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useModal } from 'react-hooks-use-modal';
import React from 'react';
import { useAllowList } from '../hooks/useAllowList';

export const useRequestAllowListFormModal= () => {
const [Modal, open, close, isOpen] = useModal('__next');
const { sendRequestAllowList } = useAllowList();

const modalStyle: React.CSSProperties = {
backgroundColor: '#fff',
padding: '60px 100px',
borderRadius: '10px',
};

async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const target = e.target as typeof e.target & {
address: { value: string };
email: { value: string };
};
const address = target.address.value;
const email = target.email.value;
await sendRequestAllowList(address, email);
close();
};

function RequestAllowListFormModal() {
return (
<Modal>
<div style={modalStyle}>
<form onSubmit={onSubmit} className="flex flex-col">
<input id="address" type="text" placeholder="Address" className="p-2 border rounded soild"/>
<input id="email" type="text" placeholder="hogemoge@example.com" className="p-2 mt-4 border rounded solid" />
<button className="p-2 mt-4 text-white bg-blue-400 border border-solid rounded">申し込み</button>
</form>
</div>
</Modal>
);
}
return {
RequestAllowListFormModal,
openRequestAllowListFormModal: open,
closeRequestAllowListFormModal: close,
}
}
110 changes: 110 additions & 0 deletions frontend/hooks/useTutorialToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { ethers } from "ethers";
import TutorialTokenArtifact from "../contracts/TutorialToken.json";
import contractAddress from "../contracts/contract-address.json";
import { db } from '../lib/firebase';
import { doc, getDocs, query, collection } from "firebase/firestore";
import { StandardMerkleTree } from "@openzeppelin/merkle-tree";

type useTutorialTokenReturnType = () => {
preMint: (address: string, callBack: () => {}) => Promise<void>;
publicMint: (callBack: () => {}) => Promise<void>;
totalSuply: () => Promise<number>;
tokenOfOwnerByIndex: () => Promise<number>;
getTokenIdList: () => Promise<number[]>;
setPreSale: (preSale: boolean) => Promise<void>;
setPublicSale: (publicSale: boolean) => Promise<void>;
}

export const useTutorialToken: useTutorialTokenReturnType = () => {
let provider: ethers.providers.Web3Provider|null = null;
let token: ethers.Contract|null = null;

async function _initializeEthers() {
provider = new ethers.providers.Web3Provider(window.ethereum);

token = await new ethers.Contract(
contractAddress.TutorialToken,
TutorialTokenArtifact.abi,
provider.getSigner(),
);
console.log(token);
}

async function preMint(address: string, callBack: () => {}): Promise<void> {
if (token === null && provider === null) await _initializeEthers();
const q = query(collection(db, "allowList"));
const querySnapshot = await getDocs(q);
const treeValues = [];
querySnapshot.forEach((doc) => {
const data = doc.data();
data.address && treeValues.push([data.address]);
});
const tree = StandardMerkleTree.of(treeValues, ["address"]);
let proof: string[]|undefined;
for (const [i, v] of tree.entries()) {
if (v[0] === address) {
proof = tree.getProof(i);
}
}
console.log(proof);
const transaction = await token.preMint(proof);
const res = await transaction.wait();
console.log(res);
callBack();
}

async function publicMint(callBack: () => {}): Promise<void> {
if (token === null && provider === null) await _initializeEthers();
const transaction = await token.mint();
const res = await transaction.wait();
console.log(res);
callBack();
}

async function totalSuply(): Promise<number> {
if (token === null && provider === null) await _initializeEthers();
const totalSuply = await token.totalSupply();
console.log(totalSuply);
return totalSuply.toNumber();
}

async function tokenOfOwnerByIndex(): Promise<number> {
if (token === null && provider === null) await _initializeEthers();
const address = await provider.getSigner().getAddress();
const count = await token.tokenOfOwnerByIndex(address, 1);
console.log('tokenOfOwnerByIndex', count.toNumber());
return count.toNumber();
}

async function getTokenIdList(): Promise<number[]> {
if (token === null && provider === null) await _initializeEthers();
const address = await provider.getSigner().getAddress();
const count = await token.balanceOf(address);
const tokenIdList = [];
for (let i = 0; i < count.toNumber(); i++) {
const tokenId = await token.tokenOfOwnerByIndex(address, i);
tokenIdList.push(tokenId.toNumber());
}
return tokenIdList;
}

async function setPreSale(preSale: boolean): Promise<void> {
if (token === null && provider === null) await _initializeEthers();
await token.setPreSale(preSale);
}

async function setPublicSale(publicSale: boolean): Promise<void> {
if (token === null && provider === null) await _initializeEthers();
await token.setPublicSale(publicSale);
}

return {
preMint,
publicMint,
totalSuply,
tokenOfOwnerByIndex,
getTokenIdList,
setPreSale,
setPublicSale,
}
};
11 changes: 11 additions & 0 deletions frontend/lib/firebase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Import the functions you need from the SDKs you need
import { initializeApp } from "firebase/app";
import { getFirestore } from 'firebase/firestore';

const firebaseConfig = {};

// Initialize Firebase
const app = initializeApp(firebaseConfig);
const db = getFirestore(app);

export { app, db };
7 changes: 5 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,19 @@
"start": "next start"
},
"dependencies": {
"@openzeppelin/merkle-tree": "^1.0.1",
"ethers": "^5.7.2",
"firebase": "^9.15.0",
"next": "latest",
"react": "18.2.0",
"react-dom": "18.2.0",
"autoprefixer": "^10.4.13",
"postcss": "^8.4.20"
"react-hooks-use-modal": "^3.2.0"
},
"devDependencies": {
"@types/node": "^18.11.15",
"@types/react": "^18.0.26",
"autoprefixer": "^10.4.13",
"postcss": "^8.4.20",
"sass": "^1.56.2",
"tailwindcss": "^3.2.4",
"typescript": "^4.9.4"
Expand Down
8 changes: 8 additions & 0 deletions frontend/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import '../styles/globals.css'
import type { AppProps } from 'next/app'

function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}

export default MyApp
98 changes: 95 additions & 3 deletions frontend/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,108 @@
import Head from 'next/head';
import Layout from '../components/layout';
import Content from '../components/content';
import { useState } from 'react';
import { useConnectWallet } from '../hooks/useConnectWallet';
import { useTutorialToken } from '../hooks/useTutorialToken';
import { useRequestAllowListFormModal } from '../hooks/useRequestAllowListFormModal'

export default function Home() {
const { connectWallet } = useConnectWallet();
const { preMint, publicMint, totalSuply, getTokenIdList } = useTutorialToken();

const [userAddress, setUserAddress] = useState<string | null>(null);
const [_totalSuply, setTotalSuply] = useState<number|null>(null);
const [tokenIdList, setTokenIdList] = useState<number[]>([]);
const { RequestAllowListFormModal, openRequestAllowListFormModal, closeRequestAllowListFormModal } = useRequestAllowListFormModal();

async function clickConnectWallet() {
const address = await connectWallet();
setUserAddress(address);

setTotalSuply(await totalSuply());
setTokenIdList(await getTokenIdList());
}

async function clickPreMintButton() {
await preMint(userAddress, callBackMint);
}

async function clickPublicMintButton() {
await publicMint(callBackMint);
}

async function callBackMint() {
setTotalSuply(await totalSuply());
setTokenIdList(await getTokenIdList());
}

function renderTokenIdList() {
return tokenIdList.map((tokenId) => {
return <li key={tokenId}>{tokenId}</li>
})
}

return (
<>
<Head>
<title>dapps入門</title>
</Head>
<h1 className="text-3xl font-bold underline">
<button onClick={connectWallet} className="p-2 border border-solid">connect wallet</button>
</h1>
<Layout>
<RequestAllowListFormModal />
<h1 className="text-3xl font-bold underline">dapps Tutorial</h1>
<Content>
<div>
<p className="text-2xl font-bold">AllowList申し込みはこちら</p>
<button onClick={openRequestAllowListFormModal} className="p-2 mt-4 mb-4 text-white bg-blue-400 border border-solid rounded">AL申し込み</button>
</div>
{ userAddress ?
<p className="mt-2">あなたのAddress: {userAddress}</p>
:
<div>
<button onClick={clickConnectWallet} className="p-2 text-white bg-blue-400 border border-solid rounded">connect wallet</button>
</div>
}


<hr className="mt-4 mb-4" />

{ userAddress &&
<>
<p className="text-2xl font-bold">Mintはこちら</p>
<div className="flex mt-4">
<div className="">
<button onClick={clickPreMintButton} className="p-2 text-white bg-red-400 border border-solid rounded">preMint</button>
</div>
<div className="ml-4">
<button onClick={clickPublicMintButton} className="p-2 text-white bg-red-400 border border-solid rounded">publicMint</button>
</div>
</div>
<hr className="mt-4 mb-4" />
</>
}

{ userAddress &&
<>
<div>
<p className="text-2xl font-bold">発行済みトークン数: { _totalSuply !== null ? _totalSuply : 'metamastk未接続' }</p>
</div>

</>
}
{
tokenIdList.length > 0 &&
<>
<hr className="mt-4 mb-4" />
<div>
<p>あなたのTokenIdのリストはこちらです</p>
<ul className="max-w-md mt-2 ml-2 space-y-1 list-disc list-inside">
{renderTokenIdList()}
</ul>
</div>
</>
}
</Content>
</Layout>
</>
);
}
6 changes: 6 additions & 0 deletions frontend/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
2 changes: 1 addition & 1 deletion frontend/styles/globals.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind utilities;
Loading