Initial commit
7
.env
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
NEXT_PUBLIC_NFT_NAME=Skulls In Love
|
||||||
|
NEXT_PUBLIC_NFT_SYMBOL=SIL
|
||||||
|
NEXT_PUBLIC_MAX_SUPPLY=10000
|
||||||
|
NEXT_PUBLIC_GAS_LIMIT=285000
|
||||||
|
NEXT_PUBLIC_TWITTER_USERNAME=@your_twitter_handle
|
||||||
|
NEXT_PUBLIC_TWITTER_URL=https://twitter.com/your_twitter_handle
|
||||||
|
NEXT_PUBLIC_DISCORD_URL=https://discord.gg/your_discord_invite_code
|
||||||
9
.env.development
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
NEXT_PUBLIC_CONTRACT_ADDRESS=your_rinkeby_contract_address
|
||||||
|
NEXT_PUBLIC_SCAN_LINK=https://rinkeby.etherscan.io/address/your_rinkeby_contract_address
|
||||||
|
NEXT_PUBLIC_NETWORK_NAME=Rinkeby
|
||||||
|
NEXT_PUBLIC_NETWORK_ID=4
|
||||||
|
NEXT_PUBLIC_CHAIN=ETH
|
||||||
|
NEXT_PUBLIC_DISPLAY_COST=0.01
|
||||||
|
NEXT_PUBLIC_WEI_COST=10000000000000000
|
||||||
|
NEXT_PUBLIC_SITE_URL=http://localhost:3000
|
||||||
|
NEXT_PUBLIC_OPENSEA_URL=https://testnets.opensea.io/collection/your_opensea_testnet_collection_name
|
||||||
9
.env.production
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
NEXT_PUBLIC_CONTRACT_ADDRESS=your_contract_address
|
||||||
|
NEXT_PUBLIC_SCAN_LINK=https://polygonscan.com/token/your_contract_address
|
||||||
|
NEXT_PUBLIC_NETWORK_NAME=Polygon
|
||||||
|
NEXT_PUBLIC_NETWORK_ID=137
|
||||||
|
NEXT_PUBLIC_CHAIN=MATIC
|
||||||
|
NEXT_PUBLIC_DISPLAY_COST=50
|
||||||
|
NEXT_PUBLIC_WEI_COST=50000000000000000000
|
||||||
|
NEXT_PUBLIC_SITE_URL=your_website_url
|
||||||
|
NEXT_PUBLIC_OPENSEA_URL=https://opensea.io/collection/your_opensea_collection_name
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"extends": "next/core-web-vitals"
|
"extends": ["next/core-web-vitals", "prettier"]
|
||||||
}
|
}
|
||||||
|
|||||||
8
@types/global.d.ts
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Ensure this is treated as a module.
|
||||||
|
export {};
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
ethereum: any;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
README.md
@@ -1,34 +1,43 @@
|
|||||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
# [Next.js](https://nextjs.org/) NFT Mint dApp
|
||||||
|
|
||||||
## Getting Started
|
A simple, fast and modern dApp for minting NFTs.
|
||||||
|
|
||||||
First, run the development server:
|
Just set up some configurations and add your own **ABI**.
|
||||||
|
|
||||||
```bash
|
The actual dApp is here: [Skulls In Love](https://www.skullsin.love/)
|
||||||
npm run dev
|
|
||||||
# or
|
## Usage
|
||||||
yarn dev
|
|
||||||
|
1. Clone this project:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/kjmczk/nextjs-nft-mint-dapp.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
2. Change into the directory:
|
||||||
|
|
||||||
You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file.
|
```sh
|
||||||
|
cd nextjs-nft-mint-dapp
|
||||||
|
```
|
||||||
|
|
||||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`.
|
3. Install the dependencies:
|
||||||
|
|
||||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
```sh
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
## Learn More
|
4. Set up some configurations:
|
||||||
|
|
||||||
To learn more about Next.js, take a look at the following resources:
|
Set the values of environment variables in the `.env.development`, `.env.production`, and `.env` files to yours.
|
||||||
|
|
||||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
5. Add your **ABI**:
|
||||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
|
||||||
|
|
||||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
Copy your contract **ABI** from the [Remix](https://remix.ethereum.org/) and paste it into `contract/abi.json`.
|
||||||
|
|
||||||
## Deploy on Vercel
|
See the [Remix documentation](https://remix-ide.readthedocs.io/en/latest/run.html) for how to generate an ABI.
|
||||||
|
|
||||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
6. Run the server:
|
||||||
|
|
||||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
```sh
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
83
components/ConnectButton.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import { useWeb3React } from '@web3-react/core';
|
||||||
|
import { FaWallet } from 'react-icons/fa';
|
||||||
|
|
||||||
|
import { useContractContext } from '../context/Contract';
|
||||||
|
import { injected } from '../utils/wallet/connectors';
|
||||||
|
|
||||||
|
export default function ConnectButton() {
|
||||||
|
const { activate, setError, chainId, error } = useWeb3React();
|
||||||
|
|
||||||
|
const { isConnecting, setErrMsg, setIsConnecting } = useContractContext();
|
||||||
|
|
||||||
|
async function connectMetaMask() {
|
||||||
|
if (typeof window.ethereum !== 'undefined') {
|
||||||
|
if (!error) {
|
||||||
|
setIsConnecting(true);
|
||||||
|
try {
|
||||||
|
await activate(injected);
|
||||||
|
setIsConnecting(false);
|
||||||
|
if (
|
||||||
|
chainId &&
|
||||||
|
chainId.toString() !== process.env.NEXT_PUBLIC_NETWORK_ID
|
||||||
|
) {
|
||||||
|
setErrMsg(
|
||||||
|
`Change the network to ${process.env.NEXT_PUBLIC_NETWORK_ID}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) setError(error);
|
||||||
|
setIsConnecting(false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setErrMsg(
|
||||||
|
`Change the network to ${process.env.NEXT_PUBLIC_NETWORK_ID}.`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setErrMsg('Please install MetaMask.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex justify-center">
|
||||||
|
{isConnecting ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex justify-center items-center border-2 border-gray-500 bg-gray-800 rounded-full px-4 py-2 w-40 cursor-not-allowed"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
className="opacity-25"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="4"
|
||||||
|
></circle>
|
||||||
|
<path
|
||||||
|
className="opacity-75"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
Connecting
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex justify-center items-center space-x-2 border-2 border-gray-500 hover:border-gray-400 bg-gray-800 rounded-full px-4 py-2 w-40"
|
||||||
|
onClick={connectMetaMask}
|
||||||
|
>
|
||||||
|
<FaWallet />
|
||||||
|
<span>Connect</span>
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
7
components/Container.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
type Props = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Container({ children }: Props) {
|
||||||
|
return <div className="container mx-auto px-4">{children}</div>;
|
||||||
|
}
|
||||||
57
components/Footer.tsx
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import { FaHome, FaTwitter, FaDiscord, FaShip } from 'react-icons/fa';
|
||||||
|
|
||||||
|
import Container from './Container';
|
||||||
|
import NextLink from './NextLink';
|
||||||
|
|
||||||
|
const getCurrentYear = () => new Date().getFullYear();
|
||||||
|
|
||||||
|
export default function Footer() {
|
||||||
|
return (
|
||||||
|
<footer className="border-t">
|
||||||
|
<Container>
|
||||||
|
<div className="flex flex-col-reverse sm:flex-row justify-between items-center py-8">
|
||||||
|
<div>
|
||||||
|
© {getCurrentYear()} {process.env.NEXT_PUBLIC_NFT_NAME}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-2 mb-4 sm:mb-0">
|
||||||
|
<NextLink
|
||||||
|
href="/"
|
||||||
|
aria-label="Home"
|
||||||
|
className="bg-gray-700 hover:bg-gray-600 rounded-full p-2"
|
||||||
|
>
|
||||||
|
<FaHome />
|
||||||
|
</NextLink>
|
||||||
|
<a
|
||||||
|
href={process.env.NEXT_PUBLIC_TWITTER_URL}
|
||||||
|
aria-label={`${process.env.NEXT_PUBLIC_NFT_NAME} on Twitter`}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="bg-gray-700 hover:bg-gray-600 rounded-full p-2"
|
||||||
|
>
|
||||||
|
<FaTwitter />
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href={process.env.NEXT_PUBLIC_DISCORD_URL}
|
||||||
|
aria-label={`${process.env.NEXT_PUBLIC_NFT_NAME} on Discord`}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="bg-gray-700 hover:bg-gray-600 rounded-full p-2"
|
||||||
|
>
|
||||||
|
<FaDiscord />
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href={process.env.NEXT_PUBLIC_OPENSEA_URL}
|
||||||
|
aria-label={`${process.env.NEXT_PUBLIC_NFT_NAME} on OpenSea`}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="bg-gray-700 hover:bg-gray-600 rounded-full p-2"
|
||||||
|
>
|
||||||
|
<FaShip />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
126
components/Header.tsx
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
import Image from 'next/image';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { useWeb3React } from '@web3-react/core';
|
||||||
|
import Blockies from 'react-blockies';
|
||||||
|
import { FaTwitter, FaDiscord, FaShip } from 'react-icons/fa';
|
||||||
|
|
||||||
|
import ConnectButton from './ConnectButton';
|
||||||
|
import Container from './Container';
|
||||||
|
import NextLink from './NextLink';
|
||||||
|
import { useContractContext } from '../context/Contract';
|
||||||
|
import { injected } from '../utils/wallet/connectors';
|
||||||
|
import Logo from '../public/assets/logo.png';
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
const { activate, setError, chainId, account, active } = useWeb3React();
|
||||||
|
|
||||||
|
const { errMsg, setErrMsg } = useContractContext();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function loadInjectedWallet() {
|
||||||
|
const isAuthorized = await injected.isAuthorized();
|
||||||
|
if (isAuthorized) {
|
||||||
|
await activate(injected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof window.ethereum !== 'undefined') {
|
||||||
|
try {
|
||||||
|
loadInjectedWallet();
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) setError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activate, setError]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (active) {
|
||||||
|
if (
|
||||||
|
chainId &&
|
||||||
|
chainId.toString() !== process.env.NEXT_PUBLIC_NETWORK_ID
|
||||||
|
) {
|
||||||
|
setErrMsg(
|
||||||
|
`Change the network to ${process.env.NEXT_PUBLIC_NETWORK_ID}.`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setErrMsg('');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setErrMsg('');
|
||||||
|
}
|
||||||
|
}, [active, chainId, setErrMsg]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="sticky top-0 z-50">
|
||||||
|
<header className="bg-gray-900 border-b py-2">
|
||||||
|
<Container>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<NextLink href="/" className="text-2xl font-bold text-white">
|
||||||
|
<span className="flex items-center">
|
||||||
|
<Image
|
||||||
|
src={Logo}
|
||||||
|
alt={process.env.NEXT_PUBLIC_NFT_NAME}
|
||||||
|
width={35}
|
||||||
|
height={35}
|
||||||
|
className="rounded-full"
|
||||||
|
/>
|
||||||
|
<span className="hidden sm:block ml-2">
|
||||||
|
{process.env.NEXT_PUBLIC_NFT_NAME}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</NextLink>
|
||||||
|
|
||||||
|
<div className="flex items-center space-x-2 ml-2 sm:ml-0">
|
||||||
|
<a
|
||||||
|
href={process.env.NEXT_PUBLIC_TWITTER_URL}
|
||||||
|
aria-label={`${process.env.NEXT_PUBLIC_NFT_NAME} on Twitter`}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="bg-gray-700 hover:bg-gray-600 rounded-full p-2"
|
||||||
|
>
|
||||||
|
<FaTwitter />
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href={process.env.NEXT_PUBLIC_DISCORD_URL}
|
||||||
|
aria-label={`${process.env.NEXT_PUBLIC_NFT_NAME} on Discord`}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="bg-gray-700 hover:bg-gray-600 rounded-full p-2"
|
||||||
|
>
|
||||||
|
<FaDiscord />
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
href={process.env.NEXT_PUBLIC_OPENSEA_URL}
|
||||||
|
aria-label={`${process.env.NEXT_PUBLIC_NFT_NAME} on OpenSea`}
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="bg-gray-700 hover:bg-gray-600 rounded-full p-2"
|
||||||
|
>
|
||||||
|
<FaShip />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{active && account ? (
|
||||||
|
<span className="flex items-center space-x-2 p-2 bg-gray-700 rounded-full">
|
||||||
|
<span>
|
||||||
|
<Blockies
|
||||||
|
seed={account.toLowerCase()}
|
||||||
|
className="rounded-full"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{`${account.substring(0, 6)}...${account.substring(
|
||||||
|
account.length - 4
|
||||||
|
)}`}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<ConnectButton />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Container>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{errMsg && <div className="bg-red-400 p-4 text-center">{errMsg}</div>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
19
components/Layout.tsx
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import Footer from './Footer';
|
||||||
|
import Header from './Header';
|
||||||
|
import Meta from './Meta';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
pageTitle?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Layout({ children, pageTitle }: Props) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Meta pageTitle={pageTitle} />
|
||||||
|
<Header />
|
||||||
|
<main>{children}</main>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
74
components/Meta.tsx
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
import Head from 'next/head';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
pageTitle?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
description: `${process.env.NEXT_PUBLIC_NFT_NAME} is an NFT collection of 10,000 unique characters.`,
|
||||||
|
ogImagePath: '/assets/card-image.png',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Meta({ pageTitle }: Props) {
|
||||||
|
const router = useRouter();
|
||||||
|
const ogUrl = process.env.NEXT_PUBLIC_SITE_URL + router.asPath;
|
||||||
|
const ogType = router.pathname === '/' ? 'website' : 'article';
|
||||||
|
const ogTitle = pageTitle
|
||||||
|
? pageTitle
|
||||||
|
: 'An NFT collection of 10,000 unique characters';
|
||||||
|
const ogImage = process.env.NEXT_PUBLIC_SITE_URL + meta.ogImagePath;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Head>
|
||||||
|
<title>{`${pageTitle} | ${process.env.NEXT_PUBLIC_NFT_NAME}`}</title>
|
||||||
|
<link
|
||||||
|
rel="apple-touch-icon"
|
||||||
|
sizes="180x180"
|
||||||
|
href="/favicon/apple-touch-icon.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="32x32"
|
||||||
|
href="/favicon/favicon-32x32.png"
|
||||||
|
/>
|
||||||
|
<link
|
||||||
|
rel="icon"
|
||||||
|
type="image/png"
|
||||||
|
sizes="16x16"
|
||||||
|
href="/favicon/favicon-16x16.png"
|
||||||
|
/>
|
||||||
|
<link rel="manifest" href="/favicon/site.webmanifest" />
|
||||||
|
<link
|
||||||
|
rel="mask-icon"
|
||||||
|
href="/favicon/safari-pinned-tab.svg"
|
||||||
|
color="#5bbad5"
|
||||||
|
/>
|
||||||
|
<link rel="shortcut icon" href="/favicon/favicon.ico" />
|
||||||
|
<meta name="msapplication-TileColor" content="#ffc40d" />
|
||||||
|
<meta name="msapplication-config" content="/favicon/browserconfig.xml" />
|
||||||
|
<meta name="theme-color" content="#ffffff" />
|
||||||
|
<link rel="alternate" type="application/rss+xml" href="/feed.xml" />
|
||||||
|
<meta name="description" content={meta.description} key="description" />
|
||||||
|
<meta property="og:url" content={ogUrl} />
|
||||||
|
<meta property="og:type" content={ogType} />
|
||||||
|
<meta
|
||||||
|
property="og:site_name"
|
||||||
|
content={process.env.NEXT_PUBLIC_NFT_NAME}
|
||||||
|
/>
|
||||||
|
<meta property="og:title" content={ogTitle} />
|
||||||
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content={meta.description}
|
||||||
|
key="ogDescription"
|
||||||
|
/>
|
||||||
|
<meta property="og:image" content={ogImage} key="ogImage" />
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta
|
||||||
|
name="twitter:site"
|
||||||
|
content={process.env.NEXT_PUBLIC_TWITTER_USERNAME}
|
||||||
|
/>
|
||||||
|
</Head>
|
||||||
|
);
|
||||||
|
}
|
||||||
17
components/NextLink.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
href: string;
|
||||||
|
className?: string;
|
||||||
|
'aria-label'?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function NextLink({ href, children, ...rest }: Props) {
|
||||||
|
return (
|
||||||
|
<Link href={href}>
|
||||||
|
<a {...rest}>{children}</a>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
7
components/Prose.tsx
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
type Props = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Prose({ children }: Props) {
|
||||||
|
return <div className="max-w-prose mx-auto px-4">{children}</div>;
|
||||||
|
}
|
||||||
49
context/Contract.tsx
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import {
|
||||||
|
createContext,
|
||||||
|
useContext,
|
||||||
|
useState,
|
||||||
|
Dispatch,
|
||||||
|
ReactElement,
|
||||||
|
ReactNode,
|
||||||
|
SetStateAction,
|
||||||
|
} from 'react';
|
||||||
|
|
||||||
|
type ContextProps = {
|
||||||
|
message: string;
|
||||||
|
errMsg: string;
|
||||||
|
isConnecting: boolean;
|
||||||
|
setMessage: Dispatch<SetStateAction<string>>;
|
||||||
|
setErrMsg: Dispatch<SetStateAction<string>>;
|
||||||
|
setIsConnecting: Dispatch<SetStateAction<boolean>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ContractContext = createContext({} as ContextProps);
|
||||||
|
|
||||||
|
export function ContractProvider({ children }: Props): ReactElement {
|
||||||
|
const [message, setMessage] = useState('');
|
||||||
|
const [errMsg, setErrMsg] = useState('');
|
||||||
|
const [isConnecting, setIsConnecting] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ContractContext.Provider
|
||||||
|
value={{
|
||||||
|
message,
|
||||||
|
errMsg,
|
||||||
|
isConnecting,
|
||||||
|
setMessage,
|
||||||
|
setErrMsg,
|
||||||
|
setIsConnecting,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ContractContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useContractContext(): ContextProps {
|
||||||
|
return useContext(ContractContext);
|
||||||
|
}
|
||||||
1
contract/abi.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
[]
|
||||||
2939
package-lock.json
generated
12
package.json
@@ -8,15 +8,25 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@web3-react/core": "^6.1.9",
|
||||||
|
"@web3-react/injected-connector": "^6.0.7",
|
||||||
|
"ethers": "^5.5.1",
|
||||||
"next": "12.0.4",
|
"next": "12.0.4",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-dom": "17.0.2"
|
"react-blockies": "^1.4.1",
|
||||||
|
"react-dom": "17.0.2",
|
||||||
|
"react-icons": "^4.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "16.11.10",
|
"@types/node": "16.11.10",
|
||||||
"@types/react": "17.0.37",
|
"@types/react": "17.0.37",
|
||||||
|
"@types/react-blockies": "^1.4.1",
|
||||||
|
"autoprefixer": "^10.4.0",
|
||||||
"eslint": "7.32.0",
|
"eslint": "7.32.0",
|
||||||
"eslint-config-next": "12.0.4",
|
"eslint-config-next": "12.0.4",
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"postcss": "^8.4.3",
|
||||||
|
"tailwindcss": "^2.2.19",
|
||||||
"typescript": "4.5.2"
|
"typescript": "4.5.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,23 @@
|
|||||||
import '../styles/globals.css'
|
// import '../styles/globals.css'
|
||||||
import type { AppProps } from 'next/app'
|
import 'tailwindcss/tailwind.css';
|
||||||
|
import type { AppProps } from 'next/app';
|
||||||
|
import { Web3ReactProvider } from '@web3-react/core';
|
||||||
|
import { ethers } from 'ethers';
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: AppProps) {
|
import { ContractProvider } from '../context/Contract';
|
||||||
return <Component {...pageProps} />
|
|
||||||
|
function getLibrary(provider: any) {
|
||||||
|
return new ethers.providers.Web3Provider(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MyApp
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
|
return (
|
||||||
|
<Web3ReactProvider getLibrary={getLibrary}>
|
||||||
|
<ContractProvider>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</ContractProvider>
|
||||||
|
</Web3ReactProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyApp;
|
||||||
|
|||||||
17
pages/_document.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import Document, { Html, Head, Main, NextScript } from 'next/document';
|
||||||
|
|
||||||
|
class MyDocument extends Document {
|
||||||
|
render(): React.ReactElement {
|
||||||
|
return (
|
||||||
|
<Html>
|
||||||
|
<Head />
|
||||||
|
<body className="bg-gray-900 text-gray-300">
|
||||||
|
<Main />
|
||||||
|
<NextScript />
|
||||||
|
</body>
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyDocument;
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
|
||||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
|
||||||
|
|
||||||
type Data = {
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function handler(
|
|
||||||
req: NextApiRequest,
|
|
||||||
res: NextApiResponse<Data>
|
|
||||||
) {
|
|
||||||
res.status(200).json({ name: 'John Doe' })
|
|
||||||
}
|
|
||||||
412
pages/index.tsx
@@ -1,72 +1,366 @@
|
|||||||
import type { NextPage } from 'next'
|
import type { NextPage } from 'next';
|
||||||
import Head from 'next/head'
|
import Head from 'next/head';
|
||||||
import Image from 'next/image'
|
import Image from 'next/image';
|
||||||
import styles from '../styles/Home.module.css'
|
import { useEffect, useState } from 'react';
|
||||||
|
import { useWeb3React } from '@web3-react/core';
|
||||||
|
import { ethers } from 'ethers';
|
||||||
|
import { IconContext } from 'react-icons';
|
||||||
|
import { FaMinusCircle, FaPlusCircle } from 'react-icons/fa';
|
||||||
|
|
||||||
|
import Layout from '../components/Layout';
|
||||||
|
import Prose from '../components/Prose';
|
||||||
|
import { useContractContext } from '../context/Contract';
|
||||||
|
import topImage from '../public/assets/1920x600.png';
|
||||||
|
import Creator from '../public/assets/creator.png';
|
||||||
|
|
||||||
|
import ABI from '../contract/abi.json';
|
||||||
|
|
||||||
const Home: NextPage = () => {
|
const Home: NextPage = () => {
|
||||||
|
const { chainId, account, active } = useWeb3React();
|
||||||
|
|
||||||
|
const { message, errMsg, setMessage } = useContractContext();
|
||||||
|
|
||||||
|
const [totalSupply, setTotalSupply] = useState('?');
|
||||||
|
const [isPending, setIsPending] = useState(false);
|
||||||
|
const [isMinting, setIsMinting] = useState(false);
|
||||||
|
const [mintAmount, setMintAmount] = useState(1);
|
||||||
|
|
||||||
|
async function claimNFTs() {
|
||||||
|
if (active && account && !errMsg) {
|
||||||
|
const cost = process.env.NEXT_PUBLIC_WEI_COST;
|
||||||
|
const gasLimit = process.env.NEXT_PUBLIC_GAS_LIMIT;
|
||||||
|
const totalCostWei = (Number(cost) * mintAmount).toString();
|
||||||
|
const totalGasLimit = (Number(gasLimit) * mintAmount).toString();
|
||||||
|
setMessage('');
|
||||||
|
setIsPending(true);
|
||||||
|
try {
|
||||||
|
const provider = new ethers.providers.Web3Provider(window.ethereum);
|
||||||
|
const signer = provider.getSigner();
|
||||||
|
const contract = new ethers.Contract(
|
||||||
|
process.env.NEXT_PUBLIC_CONTRACT_ADDRESS!,
|
||||||
|
ABI,
|
||||||
|
signer
|
||||||
|
);
|
||||||
|
const transaction = await contract.mint(mintAmount, {
|
||||||
|
value: totalCostWei,
|
||||||
|
gasLimit: totalGasLimit.toString(),
|
||||||
|
});
|
||||||
|
setIsPending(false);
|
||||||
|
setIsMinting(true);
|
||||||
|
await transaction.wait();
|
||||||
|
setIsMinting(false);
|
||||||
|
setMessage(
|
||||||
|
`Yay! ${mintAmount} ${
|
||||||
|
process.env.NEXT_PUBLIC_NFT_SYMBOL
|
||||||
|
} successfully sent to ${account.substring(
|
||||||
|
0,
|
||||||
|
6
|
||||||
|
)}...${account.substring(account.length - 4)}`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
setIsPending(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decrementMintAmount() {
|
||||||
|
if (mintAmount > 1) {
|
||||||
|
setMintAmount(mintAmount - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function incrementMintAmount() {
|
||||||
|
if (mintAmount < 10) {
|
||||||
|
setMintAmount(mintAmount + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchTotalSupply() {
|
||||||
|
const provider = new ethers.providers.Web3Provider(window.ethereum);
|
||||||
|
const contract = new ethers.Contract(
|
||||||
|
process.env.NEXT_PUBLIC_CONTRACT_ADDRESS!,
|
||||||
|
ABI,
|
||||||
|
provider
|
||||||
|
);
|
||||||
|
const totalSupply = await contract.totalSupply();
|
||||||
|
setTotalSupply(totalSupply.toString());
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
active &&
|
||||||
|
chainId &&
|
||||||
|
chainId.toString() === process.env.NEXT_PUBLIC_NETWORK_ID
|
||||||
|
) {
|
||||||
|
fetchTotalSupply();
|
||||||
|
} else {
|
||||||
|
setTotalSupply('?');
|
||||||
|
}
|
||||||
|
}, [active, chainId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<Layout>
|
||||||
<Head>
|
<Head>
|
||||||
<title>Create Next App</title>
|
<title>{process.env.NEXT_PUBLIC_NFT_NAME}</title>
|
||||||
<meta name="description" content="Generated by create next app" />
|
|
||||||
<link rel="icon" href="/favicon.ico" />
|
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<main className={styles.main}>
|
<Image src={topImage} alt={process.env.NEXT_PUBLIC_NFT_NAME} />
|
||||||
<h1 className={styles.title}>
|
|
||||||
Welcome to <a href="https://nextjs.org">Next.js!</a>
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p className={styles.description}>
|
<div className="bg-pink-600 py-8">
|
||||||
Get started by editing{' '}
|
<Prose>
|
||||||
<code className={styles.code}>pages/index.tsx</code>
|
<h1 className="text-5xl font-bold text-gray-100 mb-2">
|
||||||
</p>
|
{process.env.NEXT_PUBLIC_NFT_NAME}
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl">
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||||
|
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim
|
||||||
|
ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
|
||||||
|
aliquip ex ea commodo consequat. Duis aute irure dolor in
|
||||||
|
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
|
||||||
|
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
|
||||||
|
culpa qui officia deserunt mollit anim id est laborum.
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className={styles.grid}>
|
<div className="space-y-4 mt-4">
|
||||||
<a href="https://nextjs.org/docs" className={styles.card}>
|
<div className="bg-gray-800 border-dashed border-4 border-gray-400 rounded p-8 space-y-4">
|
||||||
<h2>Documentation →</h2>
|
<div className="text-3xl font-bold text-center">
|
||||||
<p>Find in-depth information about Next.js features and API.</p>
|
{totalSupply} / {process.env.NEXT_PUBLIC_MAX_SUPPLY}
|
||||||
</a>
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="text-xl">{`${process.env.NEXT_PUBLIC_DISPLAY_COST} ${process.env.NEXT_PUBLIC_CHAIN} per 1 NFT`}</p>
|
||||||
|
<p>(excluding gas fees)</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-center items-center space-x-4">
|
||||||
|
<IconContext.Provider value={{ size: '1.5em' }}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={
|
||||||
|
mintAmount === 1 ? 'text-gray-500 cursor-default' : ''
|
||||||
|
}
|
||||||
|
onClick={decrementMintAmount}
|
||||||
|
disabled={false}
|
||||||
|
>
|
||||||
|
<FaMinusCircle />
|
||||||
|
</button>
|
||||||
|
<span className="text-xl">{mintAmount}</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={
|
||||||
|
mintAmount === 10 ? 'text-gray-500 cursor-default' : ''
|
||||||
|
}
|
||||||
|
onClick={incrementMintAmount}
|
||||||
|
disabled={false}
|
||||||
|
>
|
||||||
|
<FaPlusCircle />
|
||||||
|
</button>
|
||||||
|
</IconContext.Provider>
|
||||||
|
</div>
|
||||||
|
|
||||||
<a href="https://nextjs.org/learn" className={styles.card}>
|
<div className="flex justify-center">
|
||||||
<h2>Learn →</h2>
|
{!active || errMsg ? (
|
||||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
<button
|
||||||
</a>
|
type="button"
|
||||||
|
className={`rounded px-4 py-2 bg-gray-700 font-bold w-40 cursor-not-allowed`}
|
||||||
|
disabled={true}
|
||||||
|
onClick={claimNFTs}
|
||||||
|
>
|
||||||
|
Buy
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{isPending || isMinting ? (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="flex justify-center items-center rounded px-4 py-2 bg-red-700 font-bold w-40 cursor-not-allowed"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="animate-spin -ml-1 mr-3 h-5 w-5 text-white"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
className="opacity-25"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="4"
|
||||||
|
></circle>
|
||||||
|
<path
|
||||||
|
className="opacity-75"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
{isPending && 'Pending'}
|
||||||
|
{isMinting && 'Minting'}
|
||||||
|
{!isPending && !isMinting && 'Processing'}
|
||||||
|
</button>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`rounded px-4 py-2 bg-blue-700 hover:bg-blue-600 font-bold w-40`}
|
||||||
|
onClick={claimNFTs}
|
||||||
|
>
|
||||||
|
Buy
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<a
|
{message && (
|
||||||
href="https://github.com/vercel/next.js/tree/master/examples"
|
<div className="text-green-500 text-center">{message}</div>
|
||||||
className={styles.card}
|
)}
|
||||||
>
|
{errMsg && (
|
||||||
<h2>Examples →</h2>
|
<div className="text-red-500 text-center">{errMsg}</div>
|
||||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
)}
|
||||||
</a>
|
</div>
|
||||||
|
|
||||||
<a
|
<div className="space-y-4">
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
<p>
|
||||||
className={styles.card}
|
Please make sure you are connected to the correct address and
|
||||||
>
|
the correct network (Polygon Mainnet) before purchasing. The
|
||||||
<h2>Deploy →</h2>
|
operation cannot be undone after purchase.
|
||||||
<p>
|
</p>
|
||||||
Instantly deploy your Next.js site to a public URL with Vercel.
|
<p>
|
||||||
|
We have set the gas limit to 285000 to successfully mint your
|
||||||
|
NFT. We recommend that you do not lower the gas limit.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Prose>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-yellow-600 py-8">
|
||||||
|
<Prose>
|
||||||
|
<h2 className="text-4xl text-gray-100 mb-4">FAQ</h2>
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-2xl text-gray-100 mb-2">{`How do I get a ${process.env.NEXT_PUBLIC_NFT_NAME} NFT?`}</h3>
|
||||||
|
<ol className="list-decimal pl-5 space-y-2">
|
||||||
|
<li>
|
||||||
|
Install{' '}
|
||||||
|
<a
|
||||||
|
href="https://metamask.io/"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="font-bold text-gray-800"
|
||||||
|
>
|
||||||
|
MetaMask
|
||||||
|
</a>{' '}
|
||||||
|
for your browser
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Buy <span className="text-gray-100 font-bold">MATIC</span> on
|
||||||
|
major exchanges including{' '}
|
||||||
|
<a
|
||||||
|
href="https://www.binance.com/"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="font-bold text-gray-800"
|
||||||
|
>
|
||||||
|
Binance
|
||||||
|
</a>{' '}
|
||||||
|
and{' '}
|
||||||
|
<a
|
||||||
|
href="https://www.coinbase.com/"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="font-bold text-gray-800"
|
||||||
|
>
|
||||||
|
Coinbase
|
||||||
|
</a>
|
||||||
|
, or swap <span className="text-gray-100 font-bold">ETH</span>{' '}
|
||||||
|
to <span className="text-gray-100 font-bold">MATIC</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Click the{' '}
|
||||||
|
<span className="text-gray-100 font-bold">Connect</span>{' '}
|
||||||
|
button at the top of this website to connect to your wallet
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Set the quantity you want and click the{' '}
|
||||||
|
<span className="text-gray-100 font-bold">Buy</span> button
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
Go to{' '}
|
||||||
|
<a
|
||||||
|
href="https://opensea.io/"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="font-bold text-gray-800"
|
||||||
|
>
|
||||||
|
OpenSea
|
||||||
|
</a>{' '}
|
||||||
|
to see the artwork(s) you purchased!
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Prose>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-green-600 py-8">
|
||||||
|
<Prose>
|
||||||
|
<h2 className="text-4xl text-gray-100 mb-4">Roadmap</h2>
|
||||||
|
<ul className="list-disc pl-5 space-y-2 mb-4">
|
||||||
|
<li>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||||
|
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
|
||||||
|
enim ad minim veniam, quis nostrud exercitation ullamco laboris
|
||||||
|
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
|
||||||
|
reprehenderit in voluptate velit esse cillum dolore eu fugiat
|
||||||
|
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
|
||||||
|
sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||||
|
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
|
||||||
|
enim ad minim veniam, quis nostrud exercitation ullamco laboris
|
||||||
|
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
|
||||||
|
reprehenderit in voluptate velit esse cillum dolore eu fugiat
|
||||||
|
nulla pariatur. Excepteur sint occaecat cupidatat non proident,
|
||||||
|
sunt in culpa qui officia deserunt mollit anim id est laborum.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</Prose>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-gray-800 py-8">
|
||||||
|
<Prose>
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="text-2xl text-gray-100 mb-4">Creator & Developer</h2>
|
||||||
|
<Image
|
||||||
|
src={Creator}
|
||||||
|
alt="Koji Mochizuki"
|
||||||
|
width={200}
|
||||||
|
height={200}
|
||||||
|
className="rounded-full"
|
||||||
|
/>
|
||||||
|
<p className="mt-4">
|
||||||
|
<a
|
||||||
|
href="https://twitter.com/kjmczk"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
target="_blank"
|
||||||
|
className="text-blue-400"
|
||||||
|
>
|
||||||
|
<span className="border-2 border-gray-700 hover:border-gray-600 rounded-full px-4 py-2 bg-gray-900">
|
||||||
|
@kjmczk
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</a>
|
</div>
|
||||||
</div>
|
</Prose>
|
||||||
</main>
|
</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
<footer className={styles.footer}>
|
export default Home;
|
||||||
<a
|
|
||||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Powered by{' '}
|
|
||||||
<span className={styles.logo}>
|
|
||||||
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
|
|
||||||
</span>
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Home
|
|
||||||
|
|||||||
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
BIN
public/assets/1920x600.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
public/assets/card-image.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/assets/creator.png
Normal file
|
After Width: | Height: | Size: 6.5 KiB |
BIN
public/assets/logo.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/assets/nft-calendar-logo.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 25 KiB |
BIN
public/favicon/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 982 B |
BIN
public/favicon/android-chrome-384x384.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/favicon/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 885 B |
9
public/favicon/browserconfig.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<browserconfig>
|
||||||
|
<msapplication>
|
||||||
|
<tile>
|
||||||
|
<square150x150logo src="/mstile-150x150.png"/>
|
||||||
|
<TileColor>#ffc40d</TileColor>
|
||||||
|
</tile>
|
||||||
|
</msapplication>
|
||||||
|
</browserconfig>
|
||||||
BIN
public/favicon/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 675 B |
BIN
public/favicon/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 964 B |
BIN
public/favicon/favicon.ico
Normal file
|
After Width: | Height: | Size: 7.2 KiB |
BIN
public/favicon/mstile-150x150.png
Normal file
|
After Width: | Height: | Size: 1010 B |
21
public/favicon/safari-pinned-tab.svg
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||||
|
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||||
|
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="450.000000pt" height="450.000000pt" viewBox="0 0 450.000000 450.000000"
|
||||||
|
preserveAspectRatio="xMidYMid meet">
|
||||||
|
<metadata>
|
||||||
|
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||||
|
</metadata>
|
||||||
|
<g transform="translate(0.000000,450.000000) scale(0.100000,-0.100000)"
|
||||||
|
fill="#000000" stroke="none">
|
||||||
|
<path d="M0 2250 l0 -2250 2250 0 2250 0 0 2250 0 2250 -2250 0 -2250 0 0
|
||||||
|
-2250z m1500 750 l0 -500 -250 0 -250 0 0 500 0 500 250 0 250 0 0 -500z
|
||||||
|
m2000 0 l0 -500 -250 0 -250 0 0 500 0 500 250 0 250 0 0 -500z m-1000 -750
|
||||||
|
l0 -250 -250 0 -250 0 0 250 0 250 250 0 250 0 0 -250z m-1000 -1000 l0 -250
|
||||||
|
250 0 250 0 0 250 0 250 250 0 250 0 0 -250 0 -250 250 0 250 0 0 250 0 250
|
||||||
|
250 0 250 0 0 -250 0 -250 -250 0 -250 0 0 -250 0 -250 -250 0 -250 0 0 250 0
|
||||||
|
250 -250 0 -250 0 0 -250 0 -250 -250 0 -250 0 0 250 0 250 -250 0 -250 0 0
|
||||||
|
250 0 250 250 0 250 0 0 -250z"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
19
public/favicon/site.webmanifest
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"name": "Skulls In Love",
|
||||||
|
"short_name": "SIL",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-192x192.png",
|
||||||
|
"sizes": "192x192",
|
||||||
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/android-chrome-384x384.png",
|
||||||
|
"sizes": "384x384",
|
||||||
|
"type": "image/png"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"theme_color": "#ffffff",
|
||||||
|
"background_color": "#ffffff",
|
||||||
|
"display": "standalone"
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<svg width="283" height="64" viewBox="0 0 283 64" fill="none"
|
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,116 +0,0 @@
|
|||||||
.container {
|
|
||||||
padding: 0 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main {
|
|
||||||
min-height: 100vh;
|
|
||||||
padding: 4rem 0;
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
padding: 2rem 0;
|
|
||||||
border-top: 1px solid #eaeaea;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer a {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title a {
|
|
||||||
color: #0070f3;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title a:hover,
|
|
||||||
.title a:focus,
|
|
||||||
.title a:active {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title {
|
|
||||||
margin: 0;
|
|
||||||
line-height: 1.15;
|
|
||||||
font-size: 4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title,
|
|
||||||
.description {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.description {
|
|
||||||
margin: 4rem 0;
|
|
||||||
line-height: 1.5;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.code {
|
|
||||||
background: #fafafa;
|
|
||||||
border-radius: 5px;
|
|
||||||
padding: 0.75rem;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
|
||||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
max-width: 800px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
margin: 1rem;
|
|
||||||
padding: 1.5rem;
|
|
||||||
text-align: left;
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
border: 1px solid #eaeaea;
|
|
||||||
border-radius: 10px;
|
|
||||||
transition: color 0.15s ease, border-color 0.15s ease;
|
|
||||||
max-width: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card:hover,
|
|
||||||
.card:focus,
|
|
||||||
.card:active {
|
|
||||||
color: #0070f3;
|
|
||||||
border-color: #0070f3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card h2 {
|
|
||||||
margin: 0 0 1rem 0;
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card p {
|
|
||||||
margin: 0;
|
|
||||||
font-size: 1.25rem;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
height: 1em;
|
|
||||||
margin-left: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
|
||||||
.grid {
|
|
||||||
width: 100%;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
tailwind.config.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
module.exports = {
|
||||||
|
purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
|
||||||
|
darkMode: false, // or 'media' or 'class'
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
};
|
||||||
5
utils/wallet/connectors.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { InjectedConnector } from '@web3-react/injected-connector';
|
||||||
|
|
||||||
|
export const injected = new InjectedConnector({
|
||||||
|
supportedChainIds: [1, 3, 4, 5, 42, 137, 80001],
|
||||||
|
});
|
||||||