diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index d89eb8ea..00000000 --- a/.eslintignore +++ /dev/null @@ -1,17 +0,0 @@ -*.d.ts -*.json -*.md -dist/ -node_modules/ - -# Nx/Builds -.docusaurus -.next -.nx -**/cjs/ -**/dts/ -**/esm/ -**/lib/ -**/mjs/ -**/out/ -**/templates/ diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 9cc90b97..00000000 --- a/.eslintrc.js +++ /dev/null @@ -1,180 +0,0 @@ -module.exports = { - root: true, - reportUnusedDisableDirectives: true, - overrides: [], - parserOptions: { - parser: '@typescript-eslint/parser', - tsconfigRootDir: __dirname, - project: './tsconfig.json', - }, - plugins: ['react-perf', 'relay', '@typescript-eslint', 'import'], - extends: [ - 'airbnb-typescript/base', - 'airbnb/rules/react', - 'airbnb/rules/react-a11y', - 'plugin:relay/strict', - 'next/core-web-vitals', - "prettier" - ], - rules: { - 'react/destructuring-assignment': 'off', - 'react/jsx-filename-extension': ['error', { extensions: ['.jsx', '.tsx', '.mdx'] }], - - // We utilize prop spreading - 'react/jsx-props-no-spreading': 'off', - - // We utilize class properties - 'react/state-in-constructor': 'off', - - // Dont use prop types since were using TypeScript - 'react/default-props-match-prop-types': 'off', - 'react/forbid-foreign-prop-types': 'off', - 'react/forbid-prop-types': 'off', - 'react/no-unused-prop-types': 'off', - 'react/prefer-read-only-props': 'off', - 'react/prop-types': 'off', - 'react/require-default-props': 'off', - 'react/sort-prop-types': 'off', - - // Performance: Avoid unnecessary renders - 'react-perf/jsx-no-new-array-as-prop': 'warn', - 'react-perf/jsx-no-new-function-as-prop': 'warn', - - // We prefer function declarations - 'react/function-component-definition': [ - 'error', - { namedComponents: 'function-declaration', unnamedComponents: 'function-expression' }, - ], - - // We prefer on/handle named events - 'react/jsx-handler-names': 'error', - - // We require named functions for inferred `displayName` - // This is required for memo() and forwardRef() usage - 'prefer-arrow-callback': ['error', { allowNamedFunctions: true }], - - 'react/jsx-one-expression-per-line': 'off', - - // We dont use flow - 'relay/generated-flow-types': 'off', - - // Shorthand types - '@typescript-eslint/array-type': ['error', { default: 'array' }], - '@typescript-eslint/consistent-indexed-object-style': ['error', 'record'], - '@typescript-eslint/consistent-type-definitions': ['error', 'type'], - '@typescript-eslint/method-signature-style': ['error', 'property'], - '@typescript-eslint/no-inferrable-types': 'error', - - // Forbid types - '@typescript-eslint/ban-types': 'error', - '@typescript-eslint/no-explicit-any': ['error', { fixToUnknown: true }], - '@typescript-eslint/no-invalid-void-type': 'error', - '@typescript-eslint/no-unsafe-argument': 'error', - '@typescript-eslint/no-unsafe-assignment': 'error', - '@typescript-eslint/no-unsafe-call': 'error', - '@typescript-eslint/no-unsafe-return': 'error', - - // Readability - '@typescript-eslint/adjacent-overload-signatures': 'error', - '@typescript-eslint/no-empty-interface': ['error', { allowSingleExtends: false }], - '@typescript-eslint/no-parameter-properties': 'error', - '@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error', - - // Correctness - '@typescript-eslint/switch-exhaustiveness-check': 'error', - '@typescript-eslint/no-for-in-array': 'error', - '@typescript-eslint/no-misused-new': 'error', - '@typescript-eslint/no-this-alias': 'error', - '@typescript-eslint/no-unnecessary-qualifier': 'error', - '@typescript-eslint/no-unnecessary-type-assertion': 'error', - '@typescript-eslint/no-unnecessary-type-constraint': 'error', - '@typescript-eslint/prefer-for-of': 'error', - '@typescript-eslint/prefer-literal-enum-member': 'error', - '@typescript-eslint/restrict-plus-operands': ['error', { checkCompoundAssignments: true }], - '@typescript-eslint/unified-signatures': 'error', - - // Assertions - '@typescript-eslint/consistent-type-assertions': 'error', - '@typescript-eslint/no-confusing-non-null-assertion': 'error', - '@typescript-eslint/no-extra-non-null-assertion': 'error', - '@typescript-eslint/no-non-null-assertion': 'error', - '@typescript-eslint/no-non-null-asserted-optional-chain': 'error', - '@typescript-eslint/prefer-as-const': 'error', - - // Comments - '@typescript-eslint/ban-ts-comment': ['error', { 'ts-expect-error': 'allow-with-description' }], - '@typescript-eslint/prefer-ts-expect-error': 'error', - '@typescript-eslint/triple-slash-reference': [ - 'error', - { path: 'never', types: 'never', lib: 'never' }, - ], - - // Async - 'no-void': 'off', - '@typescript-eslint/await-thenable': 'error', - '@typescript-eslint/no-floating-promises': 'error', - '@typescript-eslint/no-misused-promises': 'error', - '@typescript-eslint/promise-function-async': 'error', - - // APIs - '@typescript-eslint/prefer-includes': 'error', - '@typescript-eslint/prefer-nullish-coalescing': 'error', - '@typescript-eslint/prefer-optional-chain': 'error', - '@typescript-eslint/prefer-string-starts-ends-with': 'error', - - // Hard to migrate - // Errors for all try/catch blocks and any types from third-parties - '@typescript-eslint/no-unsafe-member-access': 'off', - - // We prefer React named imports only - 'react/jsx-uses-react': 'off', - 'react/react-in-jsx-scope': 'off', - 'import/extensions': 'off', - - // We prefer sorting imports by groups - 'import/order': [ - 'error', - { - "alphabetize": { - "order": "asc", - "caseInsensitive": true - }, - 'groups': [ - 'builtin', - 'external', - 'internal', - 'parent', - 'sibling', - 'index', - 'object', - 'type' - ], - "pathGroups": [ - { - "pattern": "react", - "group": "external", - "position": "before" - }, - { - "pattern": "@/**", - "group": "external", - "position": "after" - } - ], - "pathGroupsExcludedImportTypes": ["react"] - } - ], - - // We prefer labels to be associated with inputs - 'jsx-a11y/label-has-associated-control': ['error', { - 'required': { - 'some': ['nesting', 'id'] - } - }], - 'jsx-a11y/label-has-for': ['error', { - 'required': { - 'some': ['nesting', 'id'] - } - }] - }, -}; diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index d44e8250..00000000 --- a/.eslintrc.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "root": true, - "ignorePatterns": ["!**/*"], - "plugins": ["@nx"], - "overrides": [ - { - "files": ["*.ts", "*.tsx"], - "extends": ["plugin:@nx/typescript"], - "rules": {} - }, - { - "files": ["*.js", "*.jsx"], - "extends": ["plugin:@nx/javascript"], - "rules": {} - }, - { - "files": ["*.json"], - "parser": "jsonc-eslint-parser", - "rules": { - "@nx/dependency-checks": [ - "error", - { - "ignoredFiles": ["{projectRoot}/vite.config.{js,ts,mjs,mts}"] - } - ] - } - } - ] -} diff --git a/.github/workflows/build.js.yml b/.github/workflows/build.yml similarity index 100% rename from .github/workflows/build.js.yml rename to .github/workflows/build.yml diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 00000000..31def91a --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,25 @@ +name: Template Check +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Template Install dependencies + run: yarn install + + - name: Template Check + run: yarn run ci:check diff --git a/.github/workflows/cli.js.yml b/.github/workflows/cli.js.yml deleted file mode 100644 index db73d82a..00000000 --- a/.github/workflows/cli.js.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will do a clean installation of node dependencies, -# cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs - -name: CLI -on: - push: - branches: ['main'] - pull_request: - branches: ['main'] -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - steps: - - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - with: - node-version: ${{ matrix.node-version }} - - - name: CLI Install dependencies - run: yarn --immutable - - - name: CLI Lint Check - # When fails, please run "npm run lint" to your code - run: npm run lint diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml new file mode 100644 index 00000000..5dfb706d --- /dev/null +++ b/.github/workflows/format.yml @@ -0,0 +1,25 @@ +name: Template Format +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Template Install dependencies + run: yarn install + + - name: Template Format + run: yarn run ci:format diff --git a/.github/workflows/lint.js.yml b/.github/workflows/lint.js.yml deleted file mode 100644 index 10acfe72..00000000 --- a/.github/workflows/lint.js.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Lint -on: - push: - branches: ['main'] - pull_request: - branches: ['main'] -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - steps: - - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - with: - node-version: ${{ matrix.node-version }} - - - name: Install dependencies - run: yarn --immutable - - - name: Lint Check - # When fails, please run "npm run lint" to your code - run: npm run lint diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 00000000..5a572905 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,25 @@ +name: Template Lint +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + + - name: Template Install dependencies + run: yarn install + + - name: Template Lint + run: yarn run ci:lint diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index 16f8b897..00000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Version 🔖 - -on: - push: - branches: - - main - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -jobs: - version: - name: Release - runs-on: ubuntu-latest - environment: release - permissions: - contents: write - pull-requests: write - id-token: write - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: 'Setup' - uses: ./.github/actions/setup - - - name: Install node modules - run: yarn install --frozen-lockfile - shell: bash - - - name: Build - shell: bash - run: yarn build - - - name: Set deployment token - run: npm config set '//registry.npmjs.org/:_authToken' "${{ secrets.NPM_TOKEN }}" - - # https://github.com/changesets/action#with-publishing - - name: Handle Release Pull Request or Publish to npm - id: changesets - uses: changesets/action@v1 - with: - title: 'chore: version packages 🔖' - commit: 'chore: version packages 🔖' - publish: yarn release:publish - version: yarn release:version - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/styles.js.yml b/.github/workflows/styles.js.yml deleted file mode 100644 index 547a89e9..00000000 --- a/.github/workflows/styles.js.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Styles / Format -on: - push: - branches: ['main'] - pull_request: - branches: ['main'] -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - steps: - - uses: actions/checkout@v3 - - - name: Setup - uses: ./.github/actions/setup - with: - node-version: ${{ matrix.node-version }} - - - name: Install dependencies - run: yarn --immutable - - - name: Stylelint - run: npm run stylelint - - - name: Format Check - run: npm run format:check diff --git a/.github/workflows/test.js.yml b/.github/workflows/test.yml similarity index 100% rename from .github/workflows/test.js.yml rename to .github/workflows/test.yml diff --git a/.stylelintrc.json b/.stylelintrc.json deleted file mode 100644 index c11da6a5..00000000 --- a/.stylelintrc.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": [ - "stylelint-config-standard", - "stylelint-config-idiomatic-order" - ], - "ignoreFiles": [ - "coverage/**/*.css" - ], - "rules": { - "alpha-value-notation": "number", - "at-rule-no-unknown": [true, { - "ignoreAtRules": ["tailwind"] - }], - "property-no-vendor-prefix": null, - "selector-class-pattern": null - } -} diff --git a/app/api/README.md b/app/api/README.md index 6b96f964..a6eeb36e 100644 --- a/app/api/README.md +++ b/app/api/README.md @@ -1,25 +1 @@ # Build Onchain API Routes - -## Supported Chains /api/chains/supported - -**Method**: GET - -**Response**: JSON encoded viem chain object - -```json -[{"id":5,"network":"sepolia","name":"Sepolia"] -``` - -## Current Block Number /api/chain/blockNumber?chainId=5 - -Gets the current blocknumber from the backend. This might be used to ensure -your frontend has consistency with the backend (e.g what if the drift -between FE and BE). - -**Method**: GET - -**Response**: JSON encoded block number - -```json -{ "block": "13948678" } -``` diff --git a/app/api/chain/currentBlockNumber/route.ts b/app/api/chain/currentBlockNumber/route.ts deleted file mode 100644 index 223058cd..00000000 --- a/app/api/chain/currentBlockNumber/route.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { NextRequest, NextResponse } from 'next/server'; -import { getChainById } from '@/store/supportedChains'; -import { getRpcProviderForChain } from '@/utils/provider'; - -/** - * Handler for the /api/chain/blockNumber route, this route will return the current block number - * @param req - * @param res - */ -export async function GET(req: NextRequest): Promise { - try { - // Get the Chain Id from the request - const chainId = req.nextUrl.searchParams.get('chainId'); - if (!chainId) { - return NextResponse.json({ error: 'chainid is required' }, { status: 400 }); - } - const chain = getChainById(chainId); - if (!chain) { - return NextResponse.json({ error: 'chain not supported' }, { status: 400 }); - } - const provider = getRpcProviderForChain(chain); - const block = await provider.getBlockNumber(); - return NextResponse.json({ block: block.toString() }, { status: 200 }); - } catch (error) { - console.error('Error fetching chains:', error); - return NextResponse.json({}, { status: 500, statusText: 'Internal Server Error' }); - } -} - -export const dynamic = 'force-dynamic'; diff --git a/app/api/chains/supported/route.ts b/app/api/chains/supported/route.ts deleted file mode 100644 index aac43946..00000000 --- a/app/api/chains/supported/route.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { NextResponse } from 'next/server'; -import { getChainsForEnvironment } from '@/store/supportedChains'; - -/** - * Handler for the /api/chains/supported route, this route will return all the supported - * chains for this application. - * @param req - * @param res - */ -export async function GET(): Promise { - try { - const chains = getChainsForEnvironment(); - return NextResponse.json(chains, { status: 200 }); - } catch (error) { - console.error('Error fetching chains:', error); - return NextResponse.json({}, { status: 500, statusText: 'Internal Server Error' }); - } -} - -export const dynamic = 'force-dynamic'; diff --git a/app/api/paymaster-proxy/route.test.ts b/app/api/paymaster-proxy/route.test.ts index 72195a0c..c7796c00 100644 --- a/app/api/paymaster-proxy/route.test.ts +++ b/app/api/paymaster-proxy/route.test.ts @@ -1,6 +1,9 @@ -import { isValidAAEntrypoint, isWalletACoinbaseSmartWallet } from '@coinbase/onchainkit/wallet'; -import { NextRequest } from 'next/server'; import { paymasterClient } from '@/utils/paymasterClient'; +import { + isValidAAEntrypoint, + isWalletACoinbaseSmartWallet, +} from '@coinbase/onchainkit/wallet'; +import type { NextRequest } from 'next/server'; import { POST } from './route'; jest.mock('@coinbase/onchainkit/wallet', () => ({ @@ -17,7 +20,9 @@ describe('POST function', () => { it('should return 200 and the result on success for pm_getPaymasterStubData', async () => { (isValidAAEntrypoint as jest.Mock).mockReturnValue(true); (isWalletACoinbaseSmartWallet as jest.Mock).mockResolvedValue(true); - (paymasterClient.getPaymasterStubData as jest.Mock).mockResolvedValue('stub data result'); + (paymasterClient.getPaymasterStubData as jest.Mock).mockResolvedValue( + 'stub data result', + ); const req = { json: jest.fn().mockResolvedValue({ @@ -36,7 +41,9 @@ describe('POST function', () => { it('should return 200 and the result on success for pm_getPaymasterData', async () => { (isValidAAEntrypoint as jest.Mock).mockReturnValue(true); (isWalletACoinbaseSmartWallet as jest.Mock).mockResolvedValue(true); - (paymasterClient.getPaymasterData as jest.Mock).mockResolvedValue('paymaster data result'); + (paymasterClient.getPaymasterData as jest.Mock).mockResolvedValue( + 'paymaster data result', + ); const req = { json: jest.fn().mockResolvedValue({ diff --git a/app/api/paymaster-proxy/route.ts b/app/api/paymaster-proxy/route.ts index 00d6b03e..5eaa6574 100644 --- a/app/api/paymaster-proxy/route.ts +++ b/app/api/paymaster-proxy/route.ts @@ -1,11 +1,14 @@ -import { isValidAAEntrypoint, isWalletACoinbaseSmartWallet } from '@coinbase/onchainkit/wallet'; -import { NextRequest, NextResponse } from 'next/server'; -import { UserOperation } from 'permissionless'; import { client, paymasterClient } from '@/utils/paymasterClient'; +import { + isValidAAEntrypoint, + isWalletACoinbaseSmartWallet, +} from '@coinbase/onchainkit/wallet'; import type { IsValidAAEntrypointOptions, IsWalletACoinbaseSmartWalletOptions, } from '@coinbase/onchainkit/wallet'; +import { type NextRequest, NextResponse } from 'next/server'; +import type { UserOperation } from 'permissionless'; type PaymasterRequest = { method: string; @@ -58,6 +61,9 @@ export async function POST(req: NextRequest): Promise { return NextResponse.json({ result }, { status: 200 }); } catch (error) { console.error('Error:', error); - return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + return NextResponse.json( + { error: 'Internal Server Error' }, + { status: 500 }, + ); } } diff --git a/app/api/rpc/route.ts b/app/api/rpc/route.ts index 1f254e0b..8f712925 100644 --- a/app/api/rpc/route.ts +++ b/app/api/rpc/route.ts @@ -1,4 +1,4 @@ -import { NextRequest, NextResponse } from 'next/server'; +import { type NextRequest, NextResponse } from 'next/server'; const rpcUrl = process.env.NEXT_PRIVATE_RPC_URL; @@ -25,6 +25,9 @@ export async function POST(req: NextRequest) { }) .catch((error) => { console.error('Error:', error); - return NextResponse.json({}, { status: 500, statusText: 'Internal Server Error' }); + return NextResponse.json( + {}, + { status: 500, statusText: 'Internal Server Error' }, + ); }); } diff --git a/app/buy-me-coffee/_components/ContractAlert.tsx b/app/buy-me-coffee/_components/ContractAlert.tsx index beb11f58..3a1296e3 100644 --- a/app/buy-me-coffee/_components/ContractAlert.tsx +++ b/app/buy-me-coffee/_components/ContractAlert.tsx @@ -1,15 +1,17 @@ import { ExclamationTriangleIcon } from '@radix-ui/react-icons'; -import { Abi, parseEther } from 'viem'; +import { type Abi, parseEther } from 'viem'; -import { useAccount } from 'wagmi'; -import { UseContractReturn } from '@/hooks/contracts'; +import type { UseContractReturn } from '@/hooks/contracts'; import { useLoggedInUserCanAfford } from '@/hooks/useUserCanAfford'; +import { useAccount } from 'wagmi'; function useCanUserAfford(amount: number) { return useLoggedInUserCanAfford(parseEther(String(amount))); } -export function ContractAlertLayout({ children }: { children: React.ReactNode }) { +export function ContractAlertLayout({ + children, +}: { children: React.ReactNode }) { return (
@@ -25,12 +27,19 @@ type ContractAlertProps = { amount: number; }; -export default function ContractAlert({ contract, amount }: ContractAlertProps) { +export default function ContractAlert({ + contract, + amount, +}: ContractAlertProps) { const { isConnected } = useAccount(); const canAfford = useCanUserAfford(amount); if (!isConnected) { - return Please connect your wallet to continue.; + return ( + + Please connect your wallet to continue. + + ); } if (contract.status === 'onUnsupportedNetwork') { @@ -44,7 +53,9 @@ export default function ContractAlert({ contract, amount }: ContractAlertProps) if (contract.status === 'deactivated') { return ( - This contract has been deactivated on this chain. + + This contract has been deactivated on this chain. + ); } diff --git a/app/buy-me-coffee/_components/ContractDemo.tsx b/app/buy-me-coffee/_components/ContractDemo.tsx index 082933b1..f361a4c1 100644 --- a/app/buy-me-coffee/_components/ContractDemo.tsx +++ b/app/buy-me-coffee/_components/ContractDemo.tsx @@ -15,18 +15,20 @@ export default function BuyMeCoffeeContractDemo() { >
-

Messages from supporters

+

+ Messages from supporters +

{memos?.length > 0 && }
-
-

{collectionNameOrAddress}

+
+

{collectionNameOrAddress}

{isConnected && (

Estimated tx fee:{' '} {isLoadingFeeEstimate ? ( - + ) : ( <> {mintTxFeeEstimation} @@ -107,7 +121,9 @@ export default function MintContractDemo() {

)} -

{description}

+

+ {description} +

{contract.status === 'onUnsupportedNetwork' && } diff --git a/app/mint/_components/Guide.tsx b/app/mint/_components/Guide.tsx deleted file mode 100644 index 2ea5d6a3..00000000 --- a/app/mint/_components/Guide.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import { useMemo } from 'react'; -import CodeBlock from '@/components/code-block/CodeBlock'; -import { - useGuideScroll, - P, - H3, - H4, - Section, - Hr, - A, - TableOfContents, -} from '@/components/layout/guide'; - -const codeStep1 = `\`\`\`solidity -function publicMint(uint256 _amount) external payable whenLive { - if (block.timestamp < allowlistClose) revert PublicMintNotLive(); - ... -}`; - -const codeStep2 = `\`\`\`solidity -modifier whenLive() { - if (!live) revert MintNotLive(); - _; -}`; - -const codeStep3 = `\`\`\`solidity -if (block.timestamp < allowlistClose) revert PublicMintNotLive(); -`; - -const codeStep4 = `\`\`\`solidity -uint256 minted = _numberMinted(msg.sender) + _amount; -if (minted > maxPublicMint) revert MintExceeded(); -if (_totalMinted() + _amount > MAX_SUPPLY) revert SupplyExceeded(); -`; - -const codeStep5 = `\`\`\`solidity -if (msg.value != _amount * price) revert InsufficientPayment(); -`; - -const codeStep6 = `\`\`\`solidity -_mint(msg.sender, _amount); -`; - -export default function Guide() { - useGuideScroll(); - - const contents = useMemo( - () => [ - { - href: '#contract-summary', - label: 'Contract Summary', - }, - { - href: '#publicMint-explanation', - label: ( - <> - publicMint Explanation - - ), - }, - ], - [], - ); - - return ( -
-

Guide

-
-
-
-
-

Contract Summary

-

- The AllowlistNFT.sol smart contract is an extension of an ERC721 smart - contract. The ERC721A{' '} - implementation is an extension of the{' '} - ERC721 specification, and is - optimized for gas savings and batch operations. -

-
-
-

- publicMint Explanation -

-

- The publicMint function allows anyone to mint an NFT from the contract so - long as the mint is{' '} - - live - {' '} - and the allowlist time period{' '} - - has elapsed - - . -

- -

- The above conditions correspond to the{' '} - - live - {' '} - and{' '} - - allowlistClose - {' '} - variables, respectively. These values are stored as state variables in the contract, - and are set in the{' '} - - constructor - - . -

-

- The modifier{' '} - - whenLive - {' '} - is attached to the publicMint function, which checks the value of the{' '} - live state variable to determine whether to revert the transaction or - not. -

- -

- The owner of the contract can call the{' '} - - toggleLive - {' '} - function to flip the boolean value of live, thus enabling and disabling minting NFTs. -

-

- Assuming live is set to true, the publicMint function will - then ensure that the timestamp of the block being mined is{' '} - - greater than - {' '} - the timestamp stored in allowlistClose. Otherwise, the transaction will - revert. -

- -

- If the above check succeeds, then the requested number of NFTs to mint will be - calculated and added to the total number of NFTs the caller (msg.sender) - has minted previously. If this new total exceeds the maximum allowed NFTs to mint{' '} - - per-person - {' '} - or{' '} - - globally - - , then the transaction will revert. -

- -

- Then, the function will check the supplied amount of ETH (msg.value) to - ensure that{' '} - - enough ETH has been provided - {' '} - to mint the requested number of NFTs at the current price. -

- -

- Assuming all these checks succeed, publicMint will then{' '} - - mint - {' '} - the specified amount of NFTs to the caller address. -

- -
-
- - -
-
- ); -} diff --git a/app/mint/_components/NotConnected.tsx b/app/mint/_components/NotConnected.tsx index 662b0254..5abd6cfa 100644 --- a/app/mint/_components/NotConnected.tsx +++ b/app/mint/_components/NotConnected.tsx @@ -1,5 +1,7 @@ function NotConnected() { - return Please connect your wallet to continue.; + return ( + Please connect your wallet to continue. + ); } export default NotConnected; diff --git a/app/mint/_components/StepMintComplete.tsx b/app/mint/_components/StepMintComplete.tsx index f7445ba3..6e031df7 100644 --- a/app/mint/_components/StepMintComplete.tsx +++ b/app/mint/_components/StepMintComplete.tsx @@ -1,6 +1,6 @@ -import { useCallback } from 'react'; -import clsx from 'clsx'; import Button from '@/components/Button/Button'; +import clsx from 'clsx'; +import { useCallback } from 'react'; import { MintSteps } from './ContractDemo'; type MintCompleteStepProps = { @@ -8,7 +8,10 @@ type MintCompleteStepProps = { collectionName: string | null; }; -export default function StepMintComplete({ setMintStep, collectionName }: MintCompleteStepProps) { +export default function StepMintComplete({ + setMintStep, + collectionName, +}: MintCompleteStepProps) { const handleMintAnother = useCallback(() => { setMintStep(MintSteps.START_MINT_STEP); }, [setMintStep]); @@ -20,11 +23,11 @@ export default function StepMintComplete({ setMintStep, collectionName }: MintCo 'mb-8 bg-boat-footer-dark-gray p-8', )} > -

+

Congrats! You minted {collectionName}

🎉
-
+
It will take ~ 5 minutes to show up in your wallet
- {id && } -
-
- ); -} -\`\`\``; - -export default function NewGuide() { - useGuideScroll(); - - const contents = useMemo( - () => [ - { - href: '#introduction', - label: 'Paymasters (Sponsored Transactions)', - }, - { - href: '#choosing-paymaster', - label: 'Choose a paymaster service provider', - }, - { - href: '#setting-proxy', - label: '(Recommended) Setup your paymaster proxy', - }, - { - href: '#send-eip-5792-requests', - label: 'Send EIP-5792 Requests', - }, - ], - [], - ); - - return ( -
-

Guide to Using Smart Wallets with Sponsored Transactions

-
-
-
-
-

Paymasters (Sponsored Transactions)

-

- One of the biggest UX enhancements unlocked by Smart Wallet is the ability for app - developers to sponsor their users' transactions. If your app supports Smart Wallet, - you can start sponsoring your users' transactions by using{' '} - - standardized paymaster service communication - - enabled by new wallet RPC methods. - This code is also in our{' '} - Wagmi Smart Wallet template. -

-
-
-

1. Choose a Paymaster Service Provider

-

- As a prerequisite, you'll need to obtain a paymaster service URL from a paymaster - service provider. To be compatible with Smart Wallet, the paymaster provider you - choose must be ERC-7677-compliant. -

-

- We recommend the Coinbase Developer Platform{' '} - paymaster. You can find a full list of ERC-7677-compliant paymaster services{' '} - here. -

-

- Once you choose a paymaster service provider and obtain a paymaster service URL, you - can proceed to integration. -

-
-
-

2. (Recommended) Set Up Your Paymaster Proxy

-

- Creating an API to proxy calls to your paymaster service is important for two reasons: -

-
    -
  • It allows you to protect any API secret.
  • -
  • It allows you to add extra validation on what requests you want to sponsor.
  • -
-

Validate UserOperation

-

- Before we write our proxy, let's write a willSponsor function to add some extra - validation. The policies on many paymaster services are quite simple and limited. As - your API will be exposed on the web, you want to make sure it cannot be abused: called - to sponsor transactions you do not want to fund. The checks below are a bit tedious, - but highly recommended to be safe. See "Trust and Validation"{' '} - - here - {' '} - for more on this. The code below is built specifically for Smart Wallets; it would - need to be updated to support other smart accounts. -

- -
-
-

Create Proxy

-

- The proxy you create will need to handle the pm_getPaymasterStubData and - pm_getPaymasterData JSON-RPC requests specified by ERC-7677. -

- -
-
-

3. Send EIP-5792 Requests with a Paymaster Service Capability

-

- Once you have your paymaster service set up, you can now pass its URL along to Wagmi's - useWriteContracts hook. If you set up a proxy in your app's backend as recommended in - step (2) above, you'll want to pass in the proxy URL you created. -

- -

- That's it! Smart Wallet will handle the rest. If your paymaster service is able to - sponsor the transaction, Smart Wallet will indicate to your user that the transaction - is sponsored. -

-
-
- -
-

- See full documentation here:{' '} - Smart Wallet Documentation{' '} -

-
- ); -} diff --git a/app/paymaster-bundler/_components/PaymasterBundlerDemo.tsx b/app/paymaster-bundler/_components/PaymasterBundlerDemo.tsx index e6c48cde..77f8dbb2 100644 --- a/app/paymaster-bundler/_components/PaymasterBundlerDemo.tsx +++ b/app/paymaster-bundler/_components/PaymasterBundlerDemo.tsx @@ -1,7 +1,7 @@ +import { ContractAlertLayout } from 'app/buy-me-coffee/_components/ContractAlert'; import clsx from 'clsx'; import { useAccount } from 'wagmi'; import { useWriteContracts } from 'wagmi/experimental'; -import { ContractAlertLayout } from 'app/buy-me-coffee/_components/ContractAlert'; import isLocal from '../../../src/utils/isLocal'; import { usePaymasterBundlerContract } from '../_contracts/usePaymasterBundlerContract'; import { CallStatus } from './CallStatus'; @@ -42,10 +42,22 @@ export default function PaymasterBundlerDemo() { }; return ( -
-
+
+
-

+

Account Details

@@ -55,19 +67,29 @@ export default function PaymasterBundlerDemo() {
)}
-
+
-

+

Mint NFTs with Coinbase Paymaster

{!address && ( - Please connect your wallet to continue. + + Please connect your wallet to continue. + )}