├── public └── fpo │ ├── favicon.png │ └── social-card.jpg ├── @types └── alltypes.d.ts ├── next-env.d.ts ├── .env.local.example ├── next.config.js ├── .env ├── babel.config.js ├── styles ├── components.tsx ├── breakpoints.ts ├── README.md ├── reset.css ├── mixins.tsx ├── theme.ts └── GlobalStyles.tsx ├── pages ├── about.tsx ├── api │ └── ownedItems.ts ├── _document.tsx ├── index.tsx ├── _app.tsx ├── token │ └── [contract] │ │ └── [id].tsx └── list.tsx ├── components ├── Footer.tsx ├── Header.tsx ├── NavLink.tsx ├── AuctionsList.tsx ├── head.tsx └── Markdown.tsx ├── .gitignore ├── tsconfig.json ├── utils └── env-vars.ts ├── LICENSE ├── package.json ├── docs └── base-auction-deployment-guide.md ├── scripts └── setup.js └── README.md /public/fpo/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stalim17/create-auction-house/HEAD/public/fpo/favicon.png -------------------------------------------------------------------------------- /public/fpo/social-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stalim17/create-auction-house/HEAD/public/fpo/social-card.jpg -------------------------------------------------------------------------------- /@types/alltypes.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'remark-react' 2 | declare module 'remark-parse' 3 | declare module 'unified' 4 | declare module '*.md' -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | -------------------------------------------------------------------------------- /.env.local.example: -------------------------------------------------------------------------------- 1 | ## configured RPC_URL (mainnet/rinkeby depending on NETWORK_ID) for walletconnect 2 | ## ~ Keep this private in a .env.local file. And configure directly in vercel for production. 3 | ## NEXT_PUBLIC_RPC_URL= -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | webpack: (config) => { 3 | config.module.rules.push({ 4 | test: /\.md$/, 5 | use: 'raw-loader', 6 | }); 7 | return config; 8 | }, 9 | future: { 10 | webpack5: true, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_APP_TITLE=Create Auction House ☼☽ 2 | NEXT_PUBLIC_DEFAULT_DESCRIPTION=A permissionless Auction House with the ZORA protocol! 3 | NEXT_PUBLIC_MAINNET_CONTRACTS=0xabEFBc9fD2F806065b4f3C237d4b59D9A97Bcac7 4 | NEXT_PUBLIC_TESTNET_CONTRACTS=0x7C2668BD0D3c050703CEcC956C11Bd520c26f7d4 5 | NEXT_PUBLIC_NETWORK_ID=4 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | "next/babel", 5 | { 6 | "preset-react": { 7 | runtime: "automatic", 8 | importSource: "@emotion/react", 9 | }, 10 | }, 11 | ], 12 | ], 13 | plugins: ["@emotion/babel-plugin"], 14 | }; 15 | -------------------------------------------------------------------------------- /styles/components.tsx: -------------------------------------------------------------------------------- 1 | import styled from '@emotion/styled' 2 | 3 | export const PageWrapper = styled.section` 4 | margin: 0 auto; 5 | width: 100%; 6 | max-width: var(--content-width-md); 7 | position: relative; 8 | padding: 9 | var(--space-sm) 10 | var(--space-sm) 11 | var(--space-lg); 12 | ` -------------------------------------------------------------------------------- /pages/about.tsx: -------------------------------------------------------------------------------- 1 | import Head from '../components/head' 2 | import readMe from '../README.md' 3 | 4 | import Markdown from '../components/Markdown' 5 | import { PageWrapper } from '../styles/components' 6 | 7 | export default function About() { 8 | return ( 9 | <> 10 | 11 | 12 | 13 | 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | 3 | export const Footer = () => { 4 | return ( 5 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /styles/breakpoints.ts: -------------------------------------------------------------------------------- 1 | export const breakpoints = [ 2 | { 3 | viewport: 'mobile', 4 | width: 420 5 | },{ 6 | viewport: 'tablet', 7 | width: 768 8 | },{ 9 | viewport: 'laptop', 10 | width: 1280 11 | },{ 12 | viewport: 'desktop', 13 | width: 1440 14 | },{ 15 | viewport: 'xl', 16 | width: 1921 17 | } 18 | ] 19 | 20 | export const returnBreakpoint = (breakpointName: string) => { 21 | const bp = breakpoints.find((array) => array.viewport === breakpointName) 22 | return bp === undefined ? 0 : `${bp.width}px` 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | package-lock.json 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env.local 30 | .env.development.local 31 | .env.test.local 32 | .env.production.local 33 | 34 | # vercel 35 | .vercel 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "jsxImportSource": "@emotion/react" 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } -------------------------------------------------------------------------------- /components/Header.tsx: -------------------------------------------------------------------------------- 1 | import { css } from '@emotion/react' 2 | import { NavLink } from './NavLink' 3 | 4 | export const Header = () => { 5 | return ( 6 | <> 7 |
15 | 16 | Auctions 17 | 18 | 19 | List 20 | 21 | 22 | About 23 | 24 |
25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /pages/api/ownedItems.ts: -------------------------------------------------------------------------------- 1 | import { FetchStaticData, MediaFetchAgent } from "@zoralabs/nft-hooks"; 2 | import { NETWORK_ID, CONTRACT_ADDRESSES } from './../../utils/env-vars' 3 | 4 | module.exports = async (req: any, res: any) => { 5 | const { owner } = req.query; 6 | if (!owner) { 7 | return res.status(403).json({ failed: true }); 8 | } 9 | 10 | const fetchAgent = new MediaFetchAgent( 11 | NETWORK_ID as any 12 | ); 13 | 14 | const tokens = await FetchStaticData.fetchUserOwnedNFTs( 15 | fetchAgent, 16 | { 17 | collectionAddresses: CONTRACT_ADDRESSES 18 | ? (CONTRACT_ADDRESSES as string).split(",") 19 | : undefined, 20 | userAddress: owner, 21 | limit: 200, 22 | offset: 0, 23 | }, 24 | true 25 | ); 26 | res.status(200).json({ tokens }); 27 | }; 28 | -------------------------------------------------------------------------------- /components/NavLink.tsx: -------------------------------------------------------------------------------- 1 | import React, { Children } from "react"; 2 | import { useRouter } from "next/router"; 3 | import cx from "classnames"; 4 | import Link, { LinkProps } from "next/link"; 5 | 6 | type NavLinkProps = React.PropsWithChildren & { 7 | activeClassName?: string; 8 | }; 9 | 10 | export const NavLink = ({ 11 | children, 12 | activeClassName = "active", 13 | ...props 14 | }: NavLinkProps) => { 15 | const { asPath } = useRouter(); 16 | const child = Children.only(children) as React.ReactElement; 17 | const childClassName = child.props.className || ""; 18 | 19 | const isActive = asPath === props.href || asPath === props.as; 20 | const pathName = `${asPath}`.split('/')[1] 21 | const activePath = pathName === '' ? 'index' : pathName 22 | 23 | const className = cx(childClassName, activePath, { [activeClassName]: isActive }); 24 | 25 | return ( 26 | 27 | {React.cloneElement(child, { 28 | className: className || null 29 | })} 30 | 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /utils/env-vars.ts: -------------------------------------------------------------------------------- 1 | export const NETWORK_ID: string = process.env.NEXT_PUBLIC_NETWORK_ID!; 2 | if (!NETWORK_ID) { 3 | throw new Error("NetworkID is required."); 4 | } 5 | 6 | export const CURATOR_ID = process.env.NEXT_PUBLIC_CURATOR_ID; 7 | 8 | export const CONTRACT_ADDRESSES = 9 | process.env.NEXT_PUBLIC_NETWORK_ID === '1' 10 | ? (process.env.NEXT_PUBLIC_MAINNET_CONTRACTS as string) 11 | : (process.env.NEXT_PUBLIC_TESTNET_CONTRACTS as string) 12 | 13 | if (!CURATOR_ID && !CONTRACT_ADDRESSES) { 14 | throw new Error( 15 | "At least one of curator id or contract address is required" 16 | ); 17 | } 18 | 19 | export const APP_TITLE = process.env.NEXT_PUBLIC_APP_TITLE 20 | export const APP_DESCRIPTION = process.env.NEXT_PUBLIC_DEFAULT_DESCRIPTION || '' 21 | export const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || '' 22 | export const DEFAULT_OG_CARD = `${BASE_URL}/meta-content/social-card.jpg` 23 | export const FAVICON = `${BASE_URL}/meta-content/social-card.jpg` 24 | 25 | export const RPC_URL: string | undefined = process.env.NEXT_PUBLIC_RPC_URL; 26 | -------------------------------------------------------------------------------- /components/AuctionsList.tsx: -------------------------------------------------------------------------------- 1 | import { FetchStaticData } from "@zoralabs/nft-hooks"; 2 | import { NFTPreview } from "@zoralabs/nft-components"; 3 | import { useRouter } from "next/router"; 4 | 5 | export const AuctionsList = ({ tokens }: { tokens: any[] }) => { 6 | const router = useRouter(); 7 | 8 | return ( 9 |
10 | {tokens && 11 | tokens.map((token) => { 12 | const tokenInfo = FetchStaticData.getIndexerServerTokenInfo(token); 13 | return ( 14 | 20 | router.push( 21 | `/token/${tokenInfo.tokenContract}/${tokenInfo.tokenId}` 22 | ) 23 | } 24 | useBetaIndexer={true} 25 | /> 26 | ); 27 | })} 28 |
29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021-, Zora Labs. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-auction-house", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "dev-testnet": "cross-env NEXT_PUBLIC_NETWORK_ID=4 next dev", 8 | "dev-mainnet": "cross-env NEXT_PUBLIC_NETWORK_ID=1 next dev", 9 | "build": "next build", 10 | "start": "next start", 11 | "setup": "node scripts/setup.js" 12 | }, 13 | "dependencies": { 14 | "@emotion/react": "^11.4.0", 15 | "@emotion/styled": "^11.3.0", 16 | "@zoralabs/manage-auction-hooks": "^0.0.11", 17 | "@zoralabs/nft-components": "^0.3.2", 18 | "@zoralabs/nft-hooks": "^0.8.1", 19 | "@zoralabs/simple-wallet-provider": "^0.0.9", 20 | "classnames": "^2.3.1", 21 | "next": "10.2.3", 22 | "react": "17.0.2", 23 | "react-dom": "17.0.2", 24 | "remark-parse": "^9.0.0", 25 | "remark-react": "^8.0.0", 26 | "swr": "^0.5.6", 27 | "unified": "^9.2.1" 28 | }, 29 | "devDependencies": { 30 | "@emotion/babel-plugin": "^11.3.0", 31 | "@types/react": "17.0.9", 32 | "cross-env": "^7.0.3", 33 | "prompts": "^2.4.2", 34 | "raw-loader": "^4.0.2", 35 | "typescript": "4.3.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | class CreateAuctionHouseDocument extends Document { 4 | render() { 5 | return ( 6 | <> 7 | 18 | 19 | {/* Place any custom scripts here */} 20 | 21 |
22 | 23 | 24 | 25 | 26 | ) 27 | } 28 | } 29 | 30 | export default CreateAuctionHouseDocument 31 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import styled from "@emotion/styled"; 2 | import { CONTRACT_ADDRESSES, NETWORK_ID, APP_TITLE, CURATOR_ID } from '../utils/env-vars' 3 | import { GetStaticProps } from "next"; 4 | import Head from "../components/head"; 5 | import { PageWrapper } from "../styles/components"; 6 | import { AuctionsList } from "../components/AuctionsList"; 7 | 8 | import { 9 | FetchStaticData, 10 | MediaFetchAgent, 11 | NetworkIDs, 12 | } from "@zoralabs/nft-hooks"; 13 | 14 | export default function Home({ tokens }: { tokens: any }) { 15 | return ( 16 | 17 | 18 |

{APP_TITLE}

19 | 20 |
21 | ); 22 | } 23 | 24 | export const getStaticProps: GetStaticProps = async () => { 25 | const fetchAgent = new MediaFetchAgent( 26 | NETWORK_ID as NetworkIDs 27 | ); 28 | const contractAddress = CONTRACT_ADDRESSES as string; 29 | const tokens = await FetchStaticData.fetchZoraIndexerList(fetchAgent, { 30 | curatorAddress: CURATOR_ID as any, 31 | collectionAddresses: contractAddress ? contractAddress.split(',') : undefined, 32 | limit: 100, 33 | offset: 0, 34 | }); 35 | 36 | return { 37 | props: { 38 | tokens, 39 | }, 40 | revalidate: 60, 41 | }; 42 | }; 43 | 44 | const IndexWrapper = styled(PageWrapper)` 45 | max-width: var(--content-width-xl); 46 | `; -------------------------------------------------------------------------------- /components/head.tsx: -------------------------------------------------------------------------------- 1 | import NextHead from 'next/head' 2 | import { 3 | APP_TITLE, 4 | APP_DESCRIPTION, 5 | DEFAULT_OG_CARD, 6 | FAVICON, 7 | BASE_URL 8 | } from '../utils/env-vars' 9 | 10 | const Head = ({ 11 | title, 12 | description, 13 | url, 14 | ogImage, 15 | }: { 16 | title?: string 17 | description?: string 18 | url?: string 19 | ogImage?: string 20 | }) => ( 21 | 22 | 23 | {title ? `${APP_TITLE} | ${title}` : APP_TITLE} 24 | 25 | 26 | 27 | 28 | 29 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | ) 41 | 42 | export default Head 43 | -------------------------------------------------------------------------------- /styles/README.md: -------------------------------------------------------------------------------- 1 | # Theming your Auction House 🎨 2 | 3 | [See the NFT Components docs for more information on theming via JS](https://ourzora.github.io/nft-components/?path=/story/renderer-mediaconfiguration--page) 4 | 5 | NFT Components leverages the [Emotion css in js library:](https://emotion.sh/docs/introduction) 6 | 7 | 8 | ```bash 9 | import { css } from '@emotion/react' 10 | 11 | export const mediaConfigurationStyles = { 12 | theme: { 13 | /* 14 | Theme object 15 | */ 16 | }, 17 | useDefaultStyles: true /* This flag is true by default - if you want to style included nft components from scratch set this to false to clear out all css in js styling */, 18 | styles: { 19 | /* 20 | Styles Object: 21 | 22 | If you want to ovveride the styling in a particular nft-component use the below pattern to inject styling via emotion css. 23 | Style object below: 24 | https://github.com/ourzora/nft-components/blob/main/src/constants/style.ts#L64-L492 25 | 26 | in the below example we are modifying the wrapper styling for the Media renderer in the NFTFullPage component. 27 | https://ourzora.github.io/nft-components/?path=/story/renderer-nftfull--image 28 | 29 | fullMediaWrapper: () => css``, 30 | width: 100%; 31 | height 32 | img { 33 | width: 100%; 34 | height: 100%; 35 | object-fit: contain; 36 | } 37 | ` 38 | */ 39 | } 40 | } 41 | ``` 42 | -------------------------------------------------------------------------------- /docs/base-auction-deployment-guide.md: -------------------------------------------------------------------------------- 1 | # Base Deployment Guide for Zora Auction House 2 | 3 | Running an auction house on Base is simpler than most expect — this note collects a few details that make deployment faster and safer. 4 | 5 | --- 6 | 7 | ### 1. Network & Contracts 8 | - Confirm the `chainId` for **Base Mainnet (8453)** or **Base Sepolia (84532)**. 9 | - Update `.env` with `NEXT_PUBLIC_CHAIN_ID` and RPC endpoint before deploying. 10 | - When verifying contracts, use [BaseScan](https://basescan.org) to check constructor args and metadata. 11 | 12 | --- 13 | 14 | ### 2. Auction Flow Checklist 15 | - ✅ Token approval works on Base network 16 | - ✅ Bids emit events for subgraph indexing 17 | - ✅ Reserve price and time buffer behave as expected 18 | - ✅ Payout address verified and tested with Base explorer 19 | 20 | --- 21 | 22 | ### 3. Subgraph Integration 23 | - Clone `zora-creator-subgraph` and add Base network config. 24 | - Set start block to the first auction deployment to speed up sync. 25 | - Confirm metadata resolves for auctioned tokens. 26 | 27 | --- 28 | 29 | ### 4. UI Tips 30 | - Update currency label to `ETH on Base` for clarity. 31 | - Use Base’s blue tone (`#0052FF`) for primary buttons to align with ecosystem style. 32 | - Include a small “View on BaseScan” link after each bid confirmation. 33 | 34 | --- 35 | 36 | ### Closing Thought 37 | Zora’s auction contracts already feel solid — these small Base-specific adjustments make them production-ready for builders who want to launch trustless markets fast. 38 | 39 | > Transparency + simplicity = trust. That’s how Base auctions should feel. 40 | -------------------------------------------------------------------------------- /styles/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | 50 | /* ADDITIONAL DEFAULTS */ 51 | * { 52 | box-sizing: border-box; 53 | outline: 0; 54 | } 55 | 56 | button, 57 | a { 58 | -webkit-tap-highlight-color: rgba(255,255,255,0)!important; 59 | -webkit-appearance: none!important; 60 | } 61 | 62 | button { 63 | background-color: rgba(255,255,255,0); 64 | text-decoration: none!important; 65 | } -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/reset.css' 2 | 3 | import type { AppProps } from 'next/app' 4 | import { css } from '@emotion/css' 5 | import { NetworkIDs } from '@zoralabs/nft-hooks' 6 | import { MediaConfiguration } from '@zoralabs/nft-components' 7 | import { Web3ConfigProvider } from '@zoralabs/simple-wallet-provider' 8 | import { NETWORK_ID, RPC_URL } from '../utils/env-vars' 9 | import { mediaConfigurationStyles } from '../styles/theme' 10 | import GlobalStyles from '../styles/GlobalStyles' 11 | import { Header } from '../components/Header' 12 | import { Footer } from '../components/Footer' 13 | 14 | export default function CreateAuctionHouseApp({ 15 | Component, 16 | pageProps 17 | }: AppProps) { 18 | return ( 19 | <> 20 | 21 | 38 | 42 |
43 |
44 | 45 |
46 |