├── 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 |
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 |
23 |
24 |
25 | home
26 |
27 |
28 |
29 | transact
30 |
31 |
32 |
33 | Sign
34 |
35 |
36 |
37 | Mint
38 |
39 |
40 |
41 |
42 |
43 |
44 |
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 |
26 |
27 |
28 |
29 |
30 |
31 |
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 |
37 | To Account
38 |
39 | tx.setToAccount(e.target.value?.toString())}
45 | />
46 |
47 |
48 |
49 |
50 |
51 | Lovelace
52 |
53 | tx.setLovelace(e.target.value?.toString())}
61 | />
62 |
63 |
64 |
65 |
66 |
71 | Send
72 |
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 |
62 | Message
63 |
64 | setMessage(e.target.value?.toString())}
70 | />
71 |
72 |
73 |
74 |
75 |
{
79 | hideToaster()
80 | signMessage()
81 | }}
82 | >
83 | sign
84 |
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 |
58 | NFT name
59 |
60 | setName(e.target.value?.toString())}
66 | />
67 |
68 |
69 |
70 |
71 | {
75 | hideToaster()
76 | mintNFT()
77 | }}
78 | >
79 | mint
80 |
81 |
82 | {
86 | hideToaster()
87 | burnNFT()
88 | }}
89 | >
90 | burn
91 |
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 |
--------------------------------------------------------------------------------