├── Procfile ├── .eslintrc.json ├── assets ├── logo.png └── logo-white.png ├── public ├── favicon.png └── favicon.svg ├── pages ├── api │ └── blockfrost │ │ └── [[...all]].ts ├── _document.tsx ├── 404.tsx ├── 500.tsx ├── about.tsx ├── contact.tsx ├── licensing.tsx ├── privacy-policy.tsx ├── _app.tsx ├── transact.tsx ├── sign.tsx ├── mint.tsx └── index.tsx ├── postcss.config.js ├── styles ├── globals.css └── use-cardano-overrides.css ├── .env.template ├── tailwind.config.js ├── next.config.js ├── .gitignore ├── tsconfig.json ├── components ├── icons │ └── CommandLineIcon.tsx ├── Navigation.tsx └── Footer.tsx ├── README.md ├── layouts └── DefaultLayout.tsx ├── LICENSE ├── package.json ├── hooks └── use-transaction.ts └── lib └── minting-utils.ts /Procfile: -------------------------------------------------------------------------------- 1 | web: npm start -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/use-cardano/cardano-starter-kit/HEAD/assets/logo.png -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/use-cardano/cardano-starter-kit/HEAD/public/favicon.png -------------------------------------------------------------------------------- /assets/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/use-cardano/cardano-starter-kit/HEAD/assets/logo-white.png -------------------------------------------------------------------------------- /pages/api/blockfrost/[[...all]].ts: -------------------------------------------------------------------------------- 1 | export { blockfrostProxy as default } from "use-cardano-blockfrost-proxy" 2 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | html { 6 | @apply dark:bg-gray-800; 7 | } -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | BLOCKFROST_PROJECT_ID_TESTNET=testnetMySecretBlockFrostProjectId 2 | BLOCKFROST_PROJECT_ID_PREVIEW=previewMySecretBlockFrostProjectId 3 | BLOCKFROST_PROJECT_ID_MAINNET=mainnetMySecretBlockFrostProjectId 4 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: [ 4 | "./pages/**/*.{js,ts,jsx,tsx}", 5 | "./components/**/*.{js,ts,jsx,tsx}", 6 | "./layouts/**/*.{js,ts,jsx,tsx}", 7 | ], 8 | theme: { 9 | extend: {}, 10 | }, 11 | plugins: [], 12 | } -------------------------------------------------------------------------------- /pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from "next/document" 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | webpack: (config) => { 6 | config.experiments = { 7 | asyncWebAssembly: true, 8 | topLevelAwait: true, 9 | layers: true, // optional, with some bundlers/frameworks it doesn't work without 10 | } 11 | 12 | return config 13 | }, 14 | } 15 | 16 | module.exports = nextConfig 17 | -------------------------------------------------------------------------------- /.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 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { Inter } from "@next/font/google" 2 | 3 | const inter = Inter({ subsets: ["latin"] }) 4 | 5 | const notFoundPage = () => { 6 | return ( 7 |
8 |

9 | 404 | Not Found 10 |

11 |
12 | ) 13 | } 14 | 15 | export default notFoundPage 16 | -------------------------------------------------------------------------------- /pages/500.tsx: -------------------------------------------------------------------------------- 1 | import { Inter } from "@next/font/google" 2 | 3 | const inter = Inter({ subsets: ["latin"] }) 4 | 5 | const notFoundPage = () => { 6 | return ( 7 |
8 |

12 | 500 | Internal Server Error 13 |

14 |
15 | ) 16 | } 17 | 18 | export default notFoundPage 19 | -------------------------------------------------------------------------------- /pages/about.tsx: -------------------------------------------------------------------------------- 1 | import { CommandLineIcon } from "components/icons/CommandLineIcon" 2 | 3 | import { Inter } from "@next/font/google" 4 | 5 | const inter = Inter({ subsets: ["latin"] }) 6 | 7 | export default function About() { 8 | return ( 9 |
10 |

14 | About 15 |

16 | 17 |
18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /pages/contact.tsx: -------------------------------------------------------------------------------- 1 | import { CommandLineIcon } from "components/icons/CommandLineIcon" 2 | 3 | import { Inter } from "@next/font/google" 4 | 5 | const inter = Inter({ subsets: ["latin"] }) 6 | 7 | export default function About() { 8 | return ( 9 |
10 |

14 | Contact 15 |

16 | 17 |
18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /pages/licensing.tsx: -------------------------------------------------------------------------------- 1 | import { CommandLineIcon } from "components/icons/CommandLineIcon" 2 | 3 | import { Inter } from "@next/font/google" 4 | 5 | const inter = Inter({ subsets: ["latin"] }) 6 | 7 | export default function About() { 8 | return ( 9 |
10 |

14 | Licensing 15 |

16 | 17 |
18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /pages/privacy-policy.tsx: -------------------------------------------------------------------------------- 1 | import { CommandLineIcon } from "components/icons/CommandLineIcon" 2 | 3 | import { Inter } from "@next/font/google" 4 | 5 | const inter = Inter({ subsets: ["latin"] }) 6 | 7 | export default function PrivacyPolicy() { 8 | return ( 9 |
10 |

14 | Privacy Policy 15 |

16 | 17 |
18 |
19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "sourceMap": true, 5 | "target": "ES2020", 6 | "lib": ["dom", "dom.iterable", "esnext"], 7 | "allowJs": true, 8 | "skipLibCheck": true, 9 | "strict": true, 10 | "forceConsistentCasingInFileNames": true, 11 | "noEmit": true, 12 | "esModuleInterop": true, 13 | "module": "esnext", 14 | "moduleResolution": "node", 15 | "resolveJsonModule": true, 16 | "isolatedModules": true, 17 | "jsx": "preserve", 18 | "incremental": true 19 | }, 20 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 21 | "exclude": ["node_modules"] 22 | } 23 | -------------------------------------------------------------------------------- /components/icons/CommandLineIcon.tsx: -------------------------------------------------------------------------------- 1 | import { twMerge } from "tailwind-merge" 2 | 3 | type Props = { 4 | className?: string 5 | } 6 | 7 | export const CommandLineIcon = ({ className }: Props) => ( 8 | 16 | 21 | 22 | ) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cardano Starter Kit 2 | 3 | Build Web3 applications on Cardano with [use-cardano](https://use-cardano.alangaming.com) and [Lucid](https://lucid.spacebudz.io/) 4 | 5 | ## What's included? 6 | 7 | - [lucid-cardano](https://www.github.com/spacebudz/lucid) 8 | - [use-cardano](https://www.github.com/use-cardano/use-cardano) 9 | - [Next.js](https://nextjs.org/) with [TypeScript](https://www.typescriptlang.org/) 10 | - [Tailwind CSS](https://tailwindcss.com/) 11 | 12 | ## run instructions 13 | 14 | set up your environment variables in `.env.local` or elsewhere in your system (see `.env.template`) 15 | 16 | ```sh 17 | pnpm i && pnpm dev 18 | ``` 19 | 20 | ## Docs 21 | 22 | - [use-cardano](https://use-cardano.alangaming.com) 23 | - [Lucid](https://lucid.spacebudz.io/) 24 | 25 | ## license 26 | 27 | MIT 28 | -------------------------------------------------------------------------------- /pages/_app.tsx: -------------------------------------------------------------------------------- 1 | 2 | import "../styles/globals.css" 3 | import "use-cardano/styles/use-cardano.css" 4 | import "../styles/use-cardano-overrides.css" 5 | 6 | import type { AppProps } from "next/app" 7 | import { CardanoProvider, CardanoToaster, UseCardanoOptions } from "use-cardano" 8 | import { DefaultLayout } from "layouts/DefaultLayout" 9 | 10 | const options: UseCardanoOptions = { 11 | allowedNetworks: ["Testnet"], 12 | testnetNetwork: "Preview", 13 | node: { 14 | provider: "blockfrost-proxy", 15 | proxyUrl: "/api/blockfrost", 16 | }, 17 | } 18 | 19 | export default function App({ Component, pageProps }: AppProps) { 20 | return ( 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /layouts/DefaultLayout.tsx: -------------------------------------------------------------------------------- 1 | import { Footer } from "components/Footer" 2 | import { Navigation } from "components/Navigation" 3 | import Head from "next/head" 4 | import { PropsWithChildren } from "react" 5 | 6 | export const DefaultLayout = ({ children }: PropsWithChildren<{}>) => { 7 | return ( 8 | <> 9 | 10 | Cardano Starter Kit 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 23 |
{children}
24 | 25 |
26 |
27 | 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Alan Smithee 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cardano-web3-dapp-starter-kit", 3 | "engines": { 4 | "node": "^16.15" 5 | }, 6 | "version": "0.1.0", 7 | "private": true, 8 | "license": "MIT", 9 | "scripts": { 10 | "dev": "concurrently \"next dev -p 4202\" \"open-cli http://localhost:4202\"", 11 | "build": "next build", 12 | "start": "next start", 13 | "lint": "next lint" 14 | }, 15 | "dependencies": { 16 | "@next/font": "13.1.1", 17 | "@types/node": "18.11.18", 18 | "@types/react": "18.0.26", 19 | "@types/react-dom": "18.0.10", 20 | "eslint": "8.30.0", 21 | "eslint-config-next": "13.1.1", 22 | "http-proxy": "^1.18.1", 23 | "lucid-cardano": "^0.8.3", 24 | "next": "13.1.1", 25 | "react": "18.2.0", 26 | "react-dom": "18.2.0", 27 | "sharp": "^0.31.3", 28 | "tailwind-merge": "^1.8.1", 29 | "typescript": "4.9.4", 30 | "use-cardano": "1.0.0", 31 | "use-cardano-blockfrost-proxy": "^1.0.0" 32 | }, 33 | "devDependencies": { 34 | "autoprefixer": "^10.4.13", 35 | "concurrently": "^7.6.0", 36 | "open-cli": "^7.1.0", 37 | "postcss": "^8.4.20", 38 | "tailwindcss": "^3.2.4" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /styles/use-cardano-overrides.css: -------------------------------------------------------------------------------- 1 | .cardano-wallet-selector__button { 2 | @apply bg-blue-300 min-w-[225px] dark:bg-transparent border-blue-300 dark:border-white text-white font-bold tracking-wider p-0 min-h-0 h-10; 3 | } 4 | 5 | .cardano-wallet-selector__button img { 6 | @apply w-5 h-5; 7 | } 8 | 9 | .cardano-wallet-selector__menu { 10 | @apply dark:bg-gray-800 opacity-95; 11 | } 12 | 13 | .cardano-wallet-selector__menu__item, 14 | .cardano-wallet-selector__menu__item:last-child { 15 | @apply border-blue-300 dark:border-white; 16 | } 17 | 18 | .cardano-wallet-selector__menu__item button { 19 | @apply bg-blue-300 dark:bg-transparent font-bold tracking-wider transition-colors duration-200 ease-in-out; 20 | } 21 | 22 | .cardano-wallet-selector__menu__item button:not(:disabled):hover { 23 | @apply bg-blue-400 dark:bg-transparent; 24 | } 25 | 26 | .cardano-wallet-selector__menu__item__text, 27 | .cardano-wallet-selector__menu__item--current__checkmark, 28 | .cardano-wallet-selector__menu__item__disconnect, 29 | .cardano-wallet-selector__menu__item button { 30 | @apply text-white; 31 | } 32 | 33 | .cardano-wallet-selector__button__loading-spinner { 34 | @apply pt-0.5 pb-1; 35 | } 36 | 37 | .cardano-wallet-selector__button__loading-spinner:after { 38 | @apply dark:border-t-white dark:border-b-white border-t-gray-800 border-b-gray-800; 39 | } 40 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 16 | 17 | 19 | 20 | 23 | 30 | 31 | -------------------------------------------------------------------------------- /hooks/use-transaction.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useState } from "react" 2 | import { useCardano } from "use-cardano" 3 | 4 | const useTransaction = () => { 5 | const { isValid, lucid } = useCardano() 6 | 7 | const [successMessage, setSuccessMessage] = useState() 8 | const [error, setError] = useState() 9 | const [lovelace, setLovelace] = useState(0) 10 | const [toAccount, setToAccount] = useState("") 11 | 12 | const sendTransaction = useCallback(async () => { 13 | if (!lucid || !toAccount || !lovelace) return 14 | 15 | try { 16 | const tx = await lucid 17 | .newTx() 18 | .payToAddress(toAccount, { lovelace: BigInt(lovelace) }) 19 | .complete() 20 | 21 | const signedTx = await tx.sign().complete() 22 | 23 | const txHash = await signedTx.submit() 24 | 25 | setLovelace(0) 26 | setToAccount("") 27 | setSuccessMessage(`Transaction submitted with hash ${txHash}`) 28 | } catch (e) { 29 | if (e instanceof Error) setError(e) 30 | else console.error(e) 31 | } 32 | }, [lucid, toAccount, lovelace]) 33 | 34 | const lovelaceSetter = useCallback((value: string) => { 35 | setError(undefined) 36 | setSuccessMessage(undefined) 37 | 38 | const parsed = parseInt(value) 39 | if (isNaN(parsed)) return 40 | setLovelace(parsed) 41 | }, []) 42 | 43 | const toAccountSetter = useCallback((value: string) => { 44 | setError(undefined) 45 | setSuccessMessage(undefined) 46 | setToAccount(value) 47 | }, []) 48 | 49 | return { 50 | error, 51 | successMessage, 52 | lovelace, 53 | setLovelace: lovelaceSetter, 54 | toAccount, 55 | setToAccount: toAccountSetter, 56 | sendTransaction, 57 | canTransact: isValid && lovelace > 0 && toAccount, 58 | } 59 | } 60 | 61 | export { useTransaction } 62 | -------------------------------------------------------------------------------- /components/Navigation.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link" 2 | import { useRouter } from "next/router" 3 | import { useMemo } from "react" 4 | import { twMerge } from "tailwind-merge" 5 | import { CardanoWalletSelector } from "use-cardano" 6 | 7 | const className = 8 | "h-10 text-white font-bold tracking-widest uppercase rounded mr-2 px-6 flex items-center dark:hover:text-white bg-blue-300 dark:bg-transparent dark:hover:bg-transparent dark:shadow-none shadow shadow-blue-100s hover:shadow-none hover:bg-blue-400 transition-all duration-300 hover:underline underline-offset-4" 9 | 10 | const activeClassName = 11 | "text-black dark:text-white ark:bg-transparent dark:shadow-none dark:hover:bg-transparent dark:underline" 12 | 13 | export const Navigation = () => { 14 | const { asPath } = useRouter() 15 | 16 | const isHome = useMemo(() => asPath === "/", [asPath]) 17 | const isTransact = useMemo(() => asPath === "/transact", [asPath]) 18 | const isSign = useMemo(() => asPath === "/sign", [asPath]) 19 | const isMint = useMemo(() => asPath === "/mint", [asPath]) 20 | 21 | return ( 22 | 45 | ) 46 | } 47 | -------------------------------------------------------------------------------- /lib/minting-utils.ts: -------------------------------------------------------------------------------- 1 | import { Lucid, MintingPolicy, PolicyId, TxHash, Unit, utf8ToHex } from "lucid-cardano" 2 | 3 | interface Options { 4 | lucid: Lucid 5 | address: string 6 | name: string 7 | } 8 | 9 | // fully qualified asset name, hex encoded policy id + name 10 | const getUnit = (policyId: PolicyId, name: string): Unit => policyId + utf8ToHex(name) 11 | 12 | const getMintingPolicy = (lucid: Lucid, address: string) => { 13 | const { paymentCredential } = lucid.utils.getAddressDetails(address) 14 | 15 | const mintingPolicy: MintingPolicy = lucid.utils.nativeScriptFromJson({ 16 | type: "all", 17 | scripts: [{ type: "sig", keyHash: paymentCredential?.hash! }], 18 | }) 19 | 20 | return mintingPolicy 21 | } 22 | 23 | const getPolicyId = (lucid: Lucid, mintingPolicy: MintingPolicy) => { 24 | const policyId: PolicyId = lucid.utils.mintingPolicyToId(mintingPolicy) 25 | 26 | return policyId 27 | } 28 | 29 | export const mintNFT = async ({ lucid, address, name }: Options): Promise => { 30 | const mintingPolicy = getMintingPolicy(lucid, address) 31 | const policyId = getPolicyId(lucid, mintingPolicy) 32 | const unit = getUnit(policyId, name) 33 | 34 | const tx = await lucid 35 | .newTx() 36 | .mintAssets({ [unit]: 1n }) 37 | .attachMintingPolicy(mintingPolicy) 38 | .complete() 39 | 40 | const signedTx = await tx.sign().complete() 41 | 42 | const txHash = await signedTx.submit() 43 | 44 | return txHash 45 | } 46 | 47 | export const burnNFT = async ({ lucid, address, name }: Options): Promise => { 48 | const mintingPolicy = getMintingPolicy(lucid, address) 49 | const policyId = getPolicyId(lucid, mintingPolicy) 50 | const unit = getUnit(policyId, name) 51 | 52 | const tx = await lucid 53 | .newTx() 54 | .mintAssets({ [unit]: -1n }) 55 | .attachMintingPolicy(mintingPolicy) 56 | .complete() 57 | 58 | const signedTx = await tx.sign().complete() 59 | 60 | const txHash = await signedTx.submit() 61 | 62 | return txHash 63 | } 64 | -------------------------------------------------------------------------------- /components/Footer.tsx: -------------------------------------------------------------------------------- 1 | import LogoWhite from "assets/logo-white.png" 2 | import Logo from "assets/logo.png" 3 | import Image from "next/image" 4 | import Link from "next/link" 5 | import { useRouter } from "next/router" 6 | import { useMemo } from "react" 7 | import { twMerge } from "tailwind-merge" 8 | 9 | const linkClassName = "mr-4 hover:underline md:mr-6 underline-offset-2" 10 | const companySiteUrl = "https://use-cardano.alangaming.com/" 11 | 12 | export const Footer = () => { 13 | const { asPath } = useRouter() 14 | 15 | const isAbout = useMemo(() => asPath === "/about", [asPath]) 16 | const isPrivacyPolicy = useMemo(() => asPath === "/privacy-policy", [asPath]) 17 | const isLicensing = useMemo(() => asPath === "/licensing", [asPath]) 18 | const isContact = useMemo(() => asPath === "/contact", [asPath]) 19 | 20 | return ( 21 |
22 |
23 | 24 | 25 | use-cardano Logo 26 | 27 | 28 | 29 | 30 | 31 | use-cardano Logo 32 | 33 | 34 | 35 |
    36 |
  • 37 | 38 | About 39 | 40 |
  • 41 |
  • 42 | 46 | Privacy Policy 47 | 48 |
  • 49 |
  • 50 | 51 | Licensing 52 | 53 |
  • 54 |
  • 55 | 56 | Contact 57 | 58 |
  • 59 |
60 |
61 |
62 | 63 | © 2022{" "} 64 | 65 | use-cardano™ 66 | 67 | . All Rights Reserved. 68 | 69 |
70 | ) 71 | } 72 | -------------------------------------------------------------------------------- /pages/transact.tsx: -------------------------------------------------------------------------------- 1 | import { useTransaction } from "hooks/use-transaction" 2 | import { useEffect } from "react" 3 | import { useCardano } from "use-cardano" 4 | 5 | import { Inter } from "@next/font/google" 6 | 7 | const inter = Inter({ subsets: ["latin"] }) 8 | 9 | export default function Transact() { 10 | const { isValid, hideToaster, showToaster } = useCardano() 11 | const tx = useTransaction() 12 | 13 | useEffect(() => { 14 | if (!tx.successMessage) { 15 | hideToaster 16 | } else { 17 | showToaster("Success!", tx.successMessage) 18 | } 19 | }, [tx.successMessage, hideToaster, showToaster]) 20 | 21 | return ( 22 |
23 |

27 | Transact 28 |

29 | 30 |
31 | Using Lucid, we can easily send transactions on the Cardano blockchain. 32 |
33 | 34 |
35 |
36 | 47 |
48 | 49 |
50 | 63 |
64 | 65 |
66 | 73 | 74 |
75 | {isValid === false ? ( 76 |

77 | connect a wallet to send a transaction 78 |

79 | ) : !tx.successMessage && !tx.error && !tx.canTransact ? ( 80 |

81 | specify a lovelace amount and account to send a transaction 82 |

83 | ) : tx.error ? ( 84 |

85 | {tx.error.message} 86 |

87 | ) : null} 88 |
89 |
90 |
91 |
92 | ) 93 | } 94 | -------------------------------------------------------------------------------- /pages/sign.tsx: -------------------------------------------------------------------------------- 1 | import { utf8ToHex } from "lucid-cardano" 2 | import { useCallback, useMemo, useState } from "react" 3 | import { useCardano, utility } from "use-cardano" 4 | 5 | import { Inter } from "@next/font/google" 6 | 7 | const inter = Inter({ subsets: ["latin"] }) 8 | 9 | export default function Sign() { 10 | const { 11 | lucid, 12 | isValid, 13 | showToaster, 14 | hideToaster, 15 | account: { address }, 16 | } = useCardano() 17 | 18 | const [message, setMessage] = useState() 19 | const [isSigning, setIsSigning] = useState(false) 20 | 21 | const signMessage = useCallback(async () => { 22 | if (!lucid || !address || !message) return 23 | 24 | setIsSigning(true) 25 | 26 | try { 27 | const payload = utf8ToHex(message) 28 | 29 | const signedMessage = await lucid.newMessage(address, payload).sign() 30 | const hasSigned: boolean = lucid.verifyMessage(address, payload, signedMessage) 31 | 32 | if (!hasSigned) throw new Error("Could not sign message") 33 | 34 | showToaster("Signed message", message) 35 | setMessage(undefined) 36 | } catch (e) { 37 | if (utility.isError(e)) showToaster("Could not sign message", e.message) 38 | else if (typeof e === "string") showToaster("Could not sign message", e) 39 | } finally { 40 | setIsSigning(false) 41 | } 42 | }, [lucid, address, message, showToaster]) 43 | 44 | const canSign = useMemo(() => isValid && !isSigning && !!message, [isValid, isSigning, message]) 45 | 46 | return ( 47 |
48 |

52 | Sign a Message 53 |

54 | 55 |
56 | Using Lucid, we can sign a message on the Cardano blockchain. 57 |
58 | 59 |
60 |
61 | 72 |
73 | 74 |
75 | 85 | 86 |
87 | {isValid === false ? ( 88 |

89 | connect a wallet to send a transaction 90 |

91 | ) : isSigning ? ( 92 |

93 | Signing... 94 |

95 | ) : null} 96 |
97 |
98 |
99 |
100 | ) 101 | } 102 | -------------------------------------------------------------------------------- /pages/mint.tsx: -------------------------------------------------------------------------------- 1 | import * as utils from "lib/minting-utils" 2 | import { useCallback, useMemo, useState } from "react" 3 | import { useCardano, utility } from "use-cardano" 4 | 5 | import { Inter } from "@next/font/google" 6 | 7 | const inter = Inter({ subsets: ["latin"] }) 8 | 9 | export default function Mint() { 10 | const { lucid, account, showToaster, hideToaster } = useCardano() 11 | 12 | const [name, setName] = useState("") 13 | 14 | const mintNFT = useCallback(async () => { 15 | try { 16 | if (!lucid || !account?.address || !name) return 17 | 18 | const nftTx = await utils.mintNFT({ lucid, address: account.address, name }) 19 | 20 | showToaster("Minted NFT", `Transaction: ${nftTx}`) 21 | } catch (e) { 22 | if (utility.isError(e)) showToaster("Could not mint NFT", e.message) 23 | else if (typeof e === "string") showToaster("Could not mint NFT", e) 24 | } 25 | }, [lucid, account?.address, showToaster, name]) 26 | 27 | const burnNFT = useCallback(async () => { 28 | try { 29 | if (!lucid || !account?.address || !name) return 30 | 31 | const nftTx = await utils.burnNFT({ lucid, address: account?.address, name }) 32 | 33 | showToaster("Burned NFT", `Transaction: ${nftTx}`) 34 | } catch (e) { 35 | if (utility.isError(e)) showToaster("Could not burn NFT", e.message) 36 | else if (typeof e === "string") showToaster("Could not burn NFT", e) 37 | } 38 | }, [lucid, account?.address, showToaster, name]) 39 | 40 | const canMint = useMemo(() => lucid && account?.address && name, [lucid, account?.address, name]) 41 | 42 | return ( 43 |
44 |

48 | Mint a Token 49 |

50 | 51 |
52 | Using Lucid, we can mint a token on the Cardano blockchain. 53 |
54 | 55 |
56 |
57 | 68 |
69 | 70 |
71 | 81 | 82 | 92 |
93 |
94 |
95 | ) 96 | } 97 | -------------------------------------------------------------------------------- /pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { CommandLineIcon } from "components/icons/CommandLineIcon" 2 | 3 | import { Inter } from "@next/font/google" 4 | 5 | const inter = Inter({ subsets: ["latin"] }) 6 | 7 | export default function Home() { 8 | return ( 9 |
10 |

14 | Cardano Starter Kit 15 |

16 | 17 |

21 | Build Web3 applications on Cardano 22 |

23 | 24 |
25 | This is a starter kit for building cardano web3 applications. It uses{" "} 26 | 32 | use-cardano 33 | {" "} 34 | which is a React hook, context, and set of components for interacting with the Cardano 35 | blockchain, which is built on top of{" "} 36 | 42 | Lucid 43 | {" "} 44 | and provides a simple API for interacting with the Cardano blockchain. 45 |
46 | 47 |
48 | The aim of this project is to serve as a simple starting point, but also to help developers 49 | get into Cardano dApp development. With that said, this tech stack provides all 50 | functionality needed for writing off chain code for dApps and should scale with your needs. 51 |
52 | 53 |
54 | 55 | In an upcoming version of this boilerplate, we aim to include a DSL (Domain Specific 56 | Language) for the on-chain components of Cardano dApp development. We are still deciding 57 | on which language to use, but we are considering either{" "} 58 | 64 | Helios 65 | 66 | ,{" "} 67 | 73 | Aiken 74 | 75 | , or{" "} 76 | 82 | plu-ts 83 | 84 | . 85 | 86 |
87 | 88 |
89 | The starter project is built on top of Next.js, which is a React framework for building 90 | static and dynamic websites. It is a great choice for building dApps, as it is easy to use, 91 | and provides a lot of functionality out of the box. It also has a great developer 92 | experience, which makes it easy to get started. 93 |
94 | 95 |
96 | For styling, we are using Tailwind CSS, which is a utility-first CSS framework with a good 97 | balance of flexibility, composability, and ease of use. It is also very easy to use with 98 | Next.js, and provides a lot of functionality out of the box. 99 |
100 | 101 | 112 |
113 | ) 114 | } 115 | --------------------------------------------------------------------------------