├── .eslintrc.json ├── .github └── workflows │ ├── app.yml │ └── contracts.yml ├── .gitignore ├── .gitmodules ├── .husky └── pre-push ├── .nvmrc ├── .solhint.json ├── .vscode ├── extensions.json └── settings.json ├── README.md ├── docs └── START-CUSTOMIZING.md ├── foundry.toml ├── package.json ├── packages ├── app │ ├── .env │ ├── .eslintignore │ ├── .eslintrc.json │ ├── .gitignore │ ├── .prettierignore │ ├── README.md │ ├── codegen.yml │ ├── codegen │ │ └── subgraph.ts │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ └── robots.txt │ ├── src │ │ ├── Button.tsx │ │ ├── EthereumProviders.tsx │ │ ├── Inventory.tsx │ │ ├── MintButton.tsx │ │ ├── PendingIcon.tsx │ │ ├── cachedFetch.ts │ │ ├── contracts.ts │ │ ├── delay.ts │ │ ├── extractContractError.ts │ │ ├── firstParam.ts │ │ ├── formatAddress.ts │ │ ├── pages │ │ │ ├── _app.tsx │ │ │ └── index.tsx │ │ ├── pluralize.ts │ │ ├── promiseNotify.ts │ │ ├── switchChain.ts │ │ ├── useENS.ts │ │ ├── useIsMounted.tsx │ │ ├── usePromise.ts │ │ └── usePromiseFn.ts │ ├── tailwind.config.js │ └── tsconfig.json ├── contracts │ ├── .env │ ├── .gitignore │ ├── abi │ │ ├── Context.sol │ │ │ └── Context.abi.json │ │ ├── ERC721A.sol │ │ │ ├── ERC721A.abi.json │ │ │ └── ERC721A__IERC721Receiver.abi.json │ │ ├── ERC721Base.sol │ │ │ └── ERC721Base.abi.json │ │ ├── ExampleNFT.sol │ │ │ └── ExampleNFT.abi.json │ │ ├── IERC165.sol │ │ │ └── IERC165.abi.json │ │ ├── IERC20.sol │ │ │ └── IERC20.abi.json │ │ ├── IERC2981.sol │ │ │ └── IERC2981.abi.json │ │ ├── IERC721A.sol │ │ │ └── IERC721A.abi.json │ │ ├── IRenderer.sol │ │ │ └── IRenderer.abi.json │ │ └── Ownable.sol │ │ │ └── Ownable.abi.json │ ├── deploy.sh │ ├── deploys │ │ └── goerli │ │ │ └── ExampleNFT.json │ ├── out │ │ ├── Context.sol │ │ │ └── Context.abi.json │ │ ├── ECDSA.sol │ │ │ └── ECDSA.abi.json │ │ ├── ERC20.sol │ │ │ └── ERC20.abi.json │ │ ├── ERC721A.sol │ │ │ ├── ERC721A.abi.json │ │ │ └── ERC721A__IERC721Receiver.abi.json │ │ ├── ERC721Base.sol │ │ │ └── ERC721Base.abi.json │ │ ├── ExampleNFT.sol │ │ │ └── ExampleNFT.abi.json │ │ ├── IERC165.sol │ │ │ └── IERC165.abi.json │ │ ├── IERC20.sol │ │ │ └── IERC20.abi.json │ │ ├── IERC20Metadata.sol │ │ │ └── IERC20Metadata.abi.json │ │ ├── IERC2981.sol │ │ │ └── IERC2981.abi.json │ │ ├── IERC721A.sol │ │ │ └── IERC721A.abi.json │ │ ├── IRenderer.sol │ │ │ └── IRenderer.abi.json │ │ ├── Ownable.sol │ │ │ └── Ownable.abi.json │ │ └── Strings.sol │ │ │ └── Strings.abi.json │ ├── package.json │ ├── script │ │ └── .gitkeep │ ├── src │ │ ├── ERC721Base.sol │ │ ├── ExampleNFT.sol │ │ └── IRenderer.sol │ ├── test │ │ ├── ERC721Base.t.sol │ │ └── ExampleNFT.t.sol │ ├── tsconfig.json │ └── types │ │ ├── ERC721A.sol │ │ ├── ERC721A.js │ │ ├── ERC721A.ts │ │ ├── ERC721A__IERC721Receiver.js │ │ ├── ERC721A__IERC721Receiver.ts │ │ ├── index.js │ │ └── index.ts │ │ ├── ERC721Base.js │ │ ├── ERC721Base.ts │ │ ├── ExampleNFT.js │ │ ├── ExampleNFT.ts │ │ ├── IERC165.js │ │ ├── IERC165.ts │ │ ├── IERC20.js │ │ ├── IERC20.ts │ │ ├── IERC2981.js │ │ ├── IERC2981.ts │ │ ├── IERC721A.js │ │ ├── IERC721A.ts │ │ ├── IRenderer.js │ │ ├── IRenderer.ts │ │ ├── Ownable.js │ │ ├── Ownable.ts │ │ ├── common.js │ │ ├── common.ts │ │ ├── factories │ │ ├── ERC721A.sol │ │ │ ├── ERC721A__IERC721Receiver__factory.js │ │ │ ├── ERC721A__IERC721Receiver__factory.ts │ │ │ ├── ERC721A__factory.js │ │ │ ├── ERC721A__factory.ts │ │ │ ├── index.js │ │ │ └── index.ts │ │ ├── ERC721Base__factory.js │ │ ├── ERC721Base__factory.ts │ │ ├── ExampleNFT__factory.js │ │ ├── ExampleNFT__factory.ts │ │ ├── IERC165__factory.js │ │ ├── IERC165__factory.ts │ │ ├── IERC20__factory.js │ │ ├── IERC20__factory.ts │ │ ├── IERC2981__factory.js │ │ ├── IERC2981__factory.ts │ │ ├── IERC721A__factory.js │ │ ├── IERC721A__factory.ts │ │ ├── IRenderer__factory.js │ │ ├── IRenderer__factory.ts │ │ ├── Ownable__factory.js │ │ ├── Ownable__factory.ts │ │ ├── index.js │ │ └── index.ts │ │ ├── index.js │ │ └── index.ts └── subgraph │ ├── .gitignore │ ├── package.json │ ├── schema.graphql │ ├── src │ └── mapping.ts │ ├── subgraph-goerli.yaml │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── remappings.txt └── vercel.json /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"], 4 | "plugins": ["@typescript-eslint", "prettier", "simple-import-sort", "import"], 5 | "env": { 6 | "browser": true, 7 | "node": true 8 | }, 9 | "rules": { 10 | "prettier/prettier": "error", 11 | "simple-import-sort/imports": "error", 12 | "simple-import-sort/exports": "error", 13 | "import/first": "error", 14 | "import/newline-after-import": "error", 15 | "import/no-duplicates": "error" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/app.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: app 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | env: 9 | NODE_ENV: ci 10 | 11 | defaults: 12 | run: 13 | working-directory: ./packages/app 14 | 15 | jobs: 16 | build: 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest] 20 | node-version: [16.x] 21 | runs-on: ${{ matrix.os }} 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | with: 26 | submodules: true 27 | - name: Cache pnpm modules 28 | uses: actions/cache@v3 29 | with: 30 | path: ~/.pnpm-store 31 | key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} 32 | restore-keys: | 33 | ${{ runner.os }}- 34 | - name: Setup pnpm 35 | uses: pnpm/action-setup@v2.2.4 36 | with: 37 | version: 7 38 | - name: Setup node 39 | uses: actions/setup-node@v3 40 | with: 41 | node-version: ${{ matrix.node-version }} 42 | cache: pnpm 43 | - run: pnpm install 44 | - run: pnpm build 45 | 46 | lint: 47 | strategy: 48 | matrix: 49 | os: [ubuntu-latest] 50 | node-version: [16.x] 51 | runs-on: ${{ matrix.os }} 52 | steps: 53 | - name: Checkout 54 | uses: actions/checkout@v3 55 | with: 56 | submodules: true 57 | - name: Cache pnpm modules 58 | uses: actions/cache@v3 59 | with: 60 | path: ~/.pnpm-store 61 | key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} 62 | restore-keys: | 63 | ${{ runner.os }}- 64 | - name: Setup pnpm 65 | uses: pnpm/action-setup@v2.2.4 66 | with: 67 | version: 7 68 | - name: Setup node 69 | uses: actions/setup-node@v3 70 | with: 71 | node-version: ${{ matrix.node-version }} 72 | cache: "pnpm" 73 | - run: pnpm install 74 | - run: pnpm lint 75 | -------------------------------------------------------------------------------- /.github/workflows/contracts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: contracts 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | env: 9 | NODE_ENV: ci 10 | 11 | defaults: 12 | run: 13 | working-directory: ./packages/contracts 14 | 15 | jobs: 16 | build: 17 | strategy: 18 | matrix: 19 | os: [ubuntu-latest] 20 | node-version: [16.x] 21 | runs-on: ${{ matrix.os }} 22 | steps: 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | with: 26 | submodules: recursive 27 | - name: Cache pnpm modules 28 | uses: actions/cache@v3 29 | with: 30 | path: ~/.pnpm-store 31 | key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} 32 | restore-keys: | 33 | ${{ runner.os }}- 34 | - name: Setup pnpm 35 | uses: pnpm/action-setup@v2.2.4 36 | with: 37 | version: 7 38 | - name: Setup node 39 | uses: actions/setup-node@v3 40 | with: 41 | node-version: ${{ matrix.node-version }} 42 | cache: pnpm 43 | - name: Install Foundry 44 | uses: onbjerg/foundry-toolchain@v1 45 | with: 46 | version: nightly 47 | - run: pnpm install 48 | - run: pnpm build 49 | - name: Outdated files detected, run `pnpm build` and commit them 50 | run: | 51 | if [[ -n "$(git status --porcelain)" ]]; then 52 | git status 53 | git --no-pager diff 54 | exit 1 55 | fi 56 | 57 | test: 58 | strategy: 59 | matrix: 60 | os: [ubuntu-latest] 61 | node-version: [16.x] 62 | runs-on: ${{ matrix.os }} 63 | steps: 64 | - name: Checkout 65 | uses: actions/checkout@v3 66 | with: 67 | submodules: recursive 68 | - name: Cache pnpm modules 69 | uses: actions/cache@v3 70 | with: 71 | path: ~/.pnpm-store 72 | key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} 73 | restore-keys: | 74 | ${{ runner.os }}- 75 | - name: Setup pnpm 76 | uses: pnpm/action-setup@v2.2.4 77 | with: 78 | version: 7 79 | - name: Setup node 80 | uses: actions/setup-node@v3 81 | with: 82 | node-version: ${{ matrix.node-version }} 83 | cache: "pnpm" 84 | - name: Install Foundry 85 | uses: onbjerg/foundry-toolchain@v1 86 | with: 87 | version: nightly 88 | - run: pnpm install 89 | - run: pnpm test 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *debug.log 3 | 4 | .vercel 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "packages/contracts/lib/openzeppelin-contracts"] 2 | path = packages/contracts/lib/openzeppelin-contracts 3 | url = https://github.com/OpenZeppelin/openzeppelin-contracts 4 | [submodule "packages/contracts/lib/ERC721A"] 5 | path = packages/contracts/lib/ERC721A 6 | url = https://github.com/chiru-labs/ERC721A 7 | [submodule "packages/contracts/lib/forge-std"] 8 | path = packages/contracts/lib/forge-std 9 | url = https://github.com/foundry-rs/forge-std 10 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | pnpm lint 5 | pnpm test 6 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v16 2 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "code-complexity": ["error", 7], 5 | "compiler-version": ["error", "^0.8.9"], 6 | "const-name-snakecase": "off", 7 | "constructor-syntax": "error", 8 | "func-visibility": ["error", { "ignoreConstructors": true }], 9 | "max-line-length": ["error", 80], 10 | "not-rely-on-time": "off", 11 | "reason-string": ["warn", { "maxLength": 64 }] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. 3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp 4 | 5 | // List of extensions which should be recommended for users of this workspace. 6 | "recommendations": [ 7 | "bradlc.vscode-tailwindcss", 8 | "dbaeumer.vscode-eslint", 9 | "JuanBlanco.solidity", 10 | "redhat.vscode-yaml", 11 | "rvest.vs-code-prettier-eslint" 12 | ], 13 | // List of extensions recommended by VS Code that should not be recommended for users of this workspace. 14 | "unwantedRecommendations": [] 15 | } 16 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.defaultFormatter": "rvest.vs-code-prettier-eslint", 3 | "editor.formatOnSave": true, 4 | "editor.codeActionsOnSave": { 5 | "source.fixAll.eslint": true 6 | }, 7 | // improve monorepo handling 8 | "eslint.workingDirectories": [ 9 | { 10 | "mode": "auto" 11 | } 12 | ], 13 | "[solidity]": { 14 | "editor.defaultFormatter": "JuanBlanco.solidity" 15 | }, 16 | "solidity.packageDefaultDependenciesContractsDirectory": "packages/contracts/src", 17 | "solidity.packageDefaultDependenciesDirectory": "packages/contracts/lib", 18 | "search.exclude": { 19 | "packages/contracts/lib": true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # web3 scaffold 2 | 3 | _Quickly get up and running on web3_ 4 | 5 | This scaffold is set up as a monorepo using the incredibly fast [pnpm](https://pnpm.io/), with packages for each part of the project. All of these packages use a common [Typescript](https://www.typescriptlang.org/) foundation with [linting](https://eslint.org/) and [autoformatting](https://prettier.io/) and is best used with [VSCode](https://code.visualstudio.com/). It assumes you'll be deploying to an [EVM-compatible blockchain](https://chainlist.org/). 6 | 7 | 8 | ## Packages 9 | 10 | ### app 11 | 12 | This is the frontend of your project. It's built on [Next.js](https://nextjs.org/) using [Tailwind CSS](https://tailwindcss.com/). Data is read from the smart contract via a subgraph (below) using [urql](https://formidable.com/open-source/urql/) and autogenerated Typescript definitions with [GraphQL Code Generator](https://www.graphql-code-generator.com/). 13 | 14 | ### contracts 15 | 16 | This is where your smart contracts live. They're written in [Solidity](https://docs.soliditylang.org/) using [Foundry (forge & cast)](https://book.getfoundry.sh/) to compile, test, and deploy. Types are generated with [TypeChain](https://github.com/dethcrypto/TypeChain). 17 | 18 | ### subgraph 19 | 20 | This is the read-only backend for your project and where you can offload a lot of the heavy lifting that would traditionally be done through an eth RPC node. It's written in [AssemblyScript](https://www.assemblyscript.org/) and deployed to [The Graph](https://thegraph.com/). 21 | 22 | 23 | ## Deploying 24 | 25 | ### Vercel 26 | 27 | Vercel supports monorepos and pnpm out-of-the-box, but you'll need to set the "Root Directory" to `packages/app` either during the Vercel project setup or afterwards in the project settings. 28 | 29 | 30 | ## Misc install notes 31 | 32 | If the submodules in forge are not working, you can remove the directories and reinstall directly with forge: 33 | ``` 34 | $ forge install chiru-labs/ERC721A --no-commit 35 | 36 | $ forge install foundry-rs/forge-std --no-commit 37 | 38 | $ forge install OpenZeppelin/openzeppelin-contracts --no-commit 39 | ``` 40 | 41 | If you install any other submodules, remember to re-run the mappings: 42 | ``` 43 | $ forge remappings > remappings.txt 44 | ``` 45 | 46 | 47 | If you need to install jq for Mac (used to in the contracts/deploy.sh script), run: 48 | ``` 49 | $ brew install jq 50 | ``` -------------------------------------------------------------------------------- /docs/START-CUSTOMIZING.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | web3-scaffold works *almost* out of the box, so install and experiement. The ExampleNFT contract is already deployed and ready to interact with : ) 4 | 5 | ## First time setup 6 | 7 | From the root, run: 8 | 9 | ``` 10 | $ pnpm install 11 | ``` 12 | Next, setup your local env: 13 | - Copy `./packages/app/.env` to `./pacages/app/.env.local` 14 | - Add your alchemy key to the `NEXT_PUBLIC_ALCHEMY_API_KEY` ENV var 15 | 16 | Now start the app. From the root folder run: 17 | ``` 18 | $ pnpm run dev 19 | ``` 20 | 21 | And you should see a link to localhost in the console. Try it out! 22 | 23 | ## Customize Your Smart Contract 24 | 25 | Here's a quick guide on how to start customizing your app. 26 | 27 | 1. Setup your local ENV 28 | - Copy `./packages/contracts/.env` to `./pacages/contracts/.env.local` 29 | - Add your ENV vars: 30 | - `DEPLOYER` - Wallet address that is deploying 31 | - `DEPLOYER_PRIVATE_KEY` - Private key for this address 32 | - `CHAIN_NAME` - Defaults to `goerli` 33 | - `RPC_URL` - Get this from Alchemy or Infura 34 | - `ETHERSCAN_API_KEY` - Free to create if you setup an Etherscan account 35 | 36 | 2. If you'd like a fresh git repo: 37 | - Delete the `.git` folder, and then `git init` to re-create. 38 | 3. Create/copy your custom contract to `./packages/src` 39 | - i.e. MyContract.sol 40 | 4. Run `forge build` or `forge test` from the root directory 41 | - You need to build your contract at least once 42 | 5. Update the `./packages/contracts/deploy.sh` script to use the proper contract name and any require constructor args. Below is an example: 43 | 44 | ```bash 45 | CONTRACT_NAME="MyContract" 46 | 47 | ... 48 | 49 | if [ ! -f $DEPLOY_OUTPUT ] || [ ! -s $DEPLOY_OUTPUT ]; then 50 | forge create $CONTRACT_NAME --json \ 51 | --constructor-args "0xOwnerAddress" "baseUri" \ 52 | --rpc-url=$RPC_URL \ 53 | --private-key=$DEPLOYER_PRIVATE_KEY | jq . > $DEPLOY_OUTPUT 54 | fi 55 | ``` 56 | 57 | 6. If needed, install jq 58 | - `$ brew install jq` on Mac 59 | 60 | 7. CD into `./packages/contracts` and run `./deploy.sh` 61 | - If successful you should see output similar to: 62 | 63 | ```Using goerli contract address: 0x00...``` 64 | 65 | 8. Your custom contract is now deployed! The last step here is to generate your type definitions for the front-end dapp: 66 | - from the packages/contracts folder, run `pnpm run types` 67 | 68 | ## Customizing The Dapp 69 | 1. You should have already setup your ENV, but if not, copy `.env` to `.env.local` 70 | - Add your alchemy key to the `NEXT_PUBLIC_ALCHEMY_API_KEY` ENV var 71 | 72 | 2. Open `packages/app/contracts.ts` and update this to use your contract 73 | - There are several places to update, FYI! 74 | 75 | 3. Open `packages/app/contracts.ts` 76 | - Replace mentions of `ExampleNFT` with your app name 77 | - Update imports to ensure it is using your contract 78 | 4. Open `packages/app/pages/index.tsx` and update any imports to point to your new contract 79 | 5. Open `packages/app/extractContractError.ts` and update the factory import for your smart contact 80 | 6. Open `packages/app/src/Inventory.tsx` and update the contract import 81 | 82 | At this point you're on your own! You'll find additional references to `ExampleNFT` related imports throughout the app. As you start to use these components, update them to your needs : ) 83 | 84 | ## Customizing the subgraph 85 | 86 | Coming Soon ; ) -------------------------------------------------------------------------------- /foundry.toml: -------------------------------------------------------------------------------- 1 | # Ideally this file would live inside packages/contracts, but VSCode doesn't 2 | # know how to resolve Solidity imports for the workspace without opening the 3 | # contracts directory separately. 4 | # 5 | # See https://github.com/holic/web3-scaffold/pull/23 6 | 7 | [profile.default] 8 | src = 'packages/contracts/src' 9 | test = 'packages/contracts/test' 10 | out = 'packages/contracts/out' 11 | cache_path = 'packages/contracts/cache' 12 | libs = ['packages/contracts/lib'] 13 | verbosity = 2 14 | bytecode_hash = 'none' 15 | solc_version = '0.8.13' 16 | extra_output_files = ['abi'] 17 | script = 'packages/contracts/script' 18 | 19 | [fmt] 20 | line_length = 80 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web3-scaffold", 3 | "private": true, 4 | "engines": { 5 | "node": "16", 6 | "pnpm": "7" 7 | }, 8 | "scripts": { 9 | "preinstall": "npx -y only-allow pnpm", 10 | "postinstall": "[ -z $CI ] && forge install || echo \"Skipping forge install for CI\"", 11 | "dev": "pnpm --parallel dev", 12 | "prettier": "pnpm --parallel --no-bail prettier", 13 | "lint": "pnpm --parallel --no-bail lint", 14 | "test": "pnpm --parallel --no-bail test" 15 | }, 16 | "devDependencies": { 17 | "@typescript-eslint/eslint-plugin": "^5.27.1", 18 | "@typescript-eslint/parser": "^5.27.1", 19 | "eslint": "^8.17.0", 20 | "eslint-config-prettier": "^8.5.0", 21 | "eslint-plugin-import": "^2.26.0", 22 | "eslint-plugin-prettier": "^4.0.0", 23 | "eslint-plugin-simple-import-sort": "^7.0.0", 24 | "prettier": "^2.6.2", 25 | "typescript": "^4.5.5", 26 | "husky": "^8.0.0" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/app/.env: -------------------------------------------------------------------------------- 1 | # This file is checked into git and should not contain any secret values! 2 | # 3 | # Create one of the following files to use during development: 4 | # 5 | # .env.local -- If present, next will automatically load all vars into process.ENV 6 | # .env.development.local 7 | # .env.test.local 8 | # .env.production.local 9 | # 10 | # You are welcome to use other files, but remember to add them to the .gitignore file! 11 | 12 | # Find your chainID here: https://chainlist.org/ 13 | # By default we use 5, or the Goerli network 14 | NEXT_PUBLIC_CHAIN_ID=5 15 | 16 | # Provide your ALCHEMY API Key here 17 | NEXT_PUBLIC_ALCHEMY_API_KEY= 18 | 19 | -------------------------------------------------------------------------------- /packages/app/.eslintignore: -------------------------------------------------------------------------------- 1 | codegen 2 | -------------------------------------------------------------------------------- /packages/app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next", "../../.eslintrc.json", "plugin:react-hooks/recommended"], 3 | "rules": { 4 | "react-hooks/exhaustive-deps": [ 5 | "warn", 6 | { 7 | "additionalHooks": "^use(Promise|PromiseFn)$" 8 | } 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/app/.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 | 27 | # local env files 28 | .env.local 29 | .env.development.local 30 | .env.test.local 31 | .env.production.local 32 | 33 | # vercel 34 | .vercel 35 | 36 | # typescript 37 | *.tsbuildinfo 38 | -------------------------------------------------------------------------------- /packages/app/.prettierignore: -------------------------------------------------------------------------------- 1 | codegen 2 | -------------------------------------------------------------------------------- /packages/app/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | ``` 12 | 13 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 14 | 15 | You can start editing the page by modifying `pages/index.tsx`. The page auto-updates as you edit the file. 16 | 17 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.ts`. 18 | 19 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 20 | 21 | ## Learn More 22 | 23 | To learn more about Next.js, take a look at the following resources: 24 | 25 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 26 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 27 | 28 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 29 | 30 | ## Deploy on Vercel 31 | 32 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 33 | 34 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 35 | -------------------------------------------------------------------------------- /packages/app/codegen.yml: -------------------------------------------------------------------------------- 1 | schema: https://api.thegraph.com/subgraphs/name/holic/example-nft 2 | documents: "src/**/*.{ts,tsx,graphql}" 3 | generates: 4 | ./codegen/subgraph.ts: 5 | plugins: 6 | - "@graphql-codegen/typescript" 7 | - "@graphql-codegen/typescript-operations" 8 | - "@graphql-codegen/typescript-urql" 9 | config: 10 | gqlImport: urql#gql 11 | immutableTypes: true 12 | hooks: 13 | afterAllFileWrite: 14 | - eslint --fix 15 | -------------------------------------------------------------------------------- /packages/app/codegen/subgraph.ts: -------------------------------------------------------------------------------- 1 | import { gql } from 'urql'; 2 | import * as Urql from 'urql'; 3 | export type Maybe = T | null; 4 | export type InputMaybe = Maybe; 5 | export type Exact = { [K in keyof T]: T[K] }; 6 | export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; 7 | export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; 8 | export type Omit = Pick>; 9 | /** All built-in and custom scalars, mapped to their actual values */ 10 | export type Scalars = { 11 | ID: string; 12 | String: string; 13 | Boolean: boolean; 14 | Int: number; 15 | Float: number; 16 | BigDecimal: any; 17 | BigInt: any; 18 | Bytes: any; 19 | }; 20 | 21 | export type BlockChangedFilter = { 22 | readonly number_gte: Scalars['Int']; 23 | }; 24 | 25 | export type Block_Height = { 26 | readonly hash?: InputMaybe; 27 | readonly number?: InputMaybe; 28 | readonly number_gte?: InputMaybe; 29 | }; 30 | 31 | /** Defines the order direction, either ascending or descending */ 32 | export enum OrderDirection { 33 | Asc = 'asc', 34 | Desc = 'desc' 35 | } 36 | 37 | export type Query = { 38 | readonly __typename?: 'Query'; 39 | /** Access to subgraph metadata */ 40 | readonly _meta?: Maybe<_Meta_>; 41 | readonly token?: Maybe; 42 | readonly tokens: ReadonlyArray; 43 | }; 44 | 45 | 46 | export type Query_MetaArgs = { 47 | block?: InputMaybe; 48 | }; 49 | 50 | 51 | export type QueryTokenArgs = { 52 | block?: InputMaybe; 53 | id: Scalars['ID']; 54 | subgraphError?: _SubgraphErrorPolicy_; 55 | }; 56 | 57 | 58 | export type QueryTokensArgs = { 59 | block?: InputMaybe; 60 | first?: InputMaybe; 61 | orderBy?: InputMaybe; 62 | orderDirection?: InputMaybe; 63 | skip?: InputMaybe; 64 | subgraphError?: _SubgraphErrorPolicy_; 65 | where?: InputMaybe; 66 | }; 67 | 68 | export type Subscription = { 69 | readonly __typename?: 'Subscription'; 70 | /** Access to subgraph metadata */ 71 | readonly _meta?: Maybe<_Meta_>; 72 | readonly token?: Maybe; 73 | readonly tokens: ReadonlyArray; 74 | }; 75 | 76 | 77 | export type Subscription_MetaArgs = { 78 | block?: InputMaybe; 79 | }; 80 | 81 | 82 | export type SubscriptionTokenArgs = { 83 | block?: InputMaybe; 84 | id: Scalars['ID']; 85 | subgraphError?: _SubgraphErrorPolicy_; 86 | }; 87 | 88 | 89 | export type SubscriptionTokensArgs = { 90 | block?: InputMaybe; 91 | first?: InputMaybe; 92 | orderBy?: InputMaybe; 93 | orderDirection?: InputMaybe; 94 | skip?: InputMaybe; 95 | subgraphError?: _SubgraphErrorPolicy_; 96 | where?: InputMaybe; 97 | }; 98 | 99 | export type Token = { 100 | readonly __typename?: 'Token'; 101 | readonly id: Scalars['ID']; 102 | readonly owner: Scalars['Bytes']; 103 | readonly tokenURI: Scalars['String']; 104 | }; 105 | 106 | export type Token_Filter = { 107 | /** Filter for the block changed event. */ 108 | readonly _change_block?: InputMaybe; 109 | readonly id?: InputMaybe; 110 | readonly id_gt?: InputMaybe; 111 | readonly id_gte?: InputMaybe; 112 | readonly id_in?: InputMaybe>; 113 | readonly id_lt?: InputMaybe; 114 | readonly id_lte?: InputMaybe; 115 | readonly id_not?: InputMaybe; 116 | readonly id_not_in?: InputMaybe>; 117 | readonly owner?: InputMaybe; 118 | readonly owner_contains?: InputMaybe; 119 | readonly owner_gt?: InputMaybe; 120 | readonly owner_gte?: InputMaybe; 121 | readonly owner_in?: InputMaybe>; 122 | readonly owner_lt?: InputMaybe; 123 | readonly owner_lte?: InputMaybe; 124 | readonly owner_not?: InputMaybe; 125 | readonly owner_not_contains?: InputMaybe; 126 | readonly owner_not_in?: InputMaybe>; 127 | readonly tokenURI?: InputMaybe; 128 | readonly tokenURI_contains?: InputMaybe; 129 | readonly tokenURI_contains_nocase?: InputMaybe; 130 | readonly tokenURI_ends_with?: InputMaybe; 131 | readonly tokenURI_ends_with_nocase?: InputMaybe; 132 | readonly tokenURI_gt?: InputMaybe; 133 | readonly tokenURI_gte?: InputMaybe; 134 | readonly tokenURI_in?: InputMaybe>; 135 | readonly tokenURI_lt?: InputMaybe; 136 | readonly tokenURI_lte?: InputMaybe; 137 | readonly tokenURI_not?: InputMaybe; 138 | readonly tokenURI_not_contains?: InputMaybe; 139 | readonly tokenURI_not_contains_nocase?: InputMaybe; 140 | readonly tokenURI_not_ends_with?: InputMaybe; 141 | readonly tokenURI_not_ends_with_nocase?: InputMaybe; 142 | readonly tokenURI_not_in?: InputMaybe>; 143 | readonly tokenURI_not_starts_with?: InputMaybe; 144 | readonly tokenURI_not_starts_with_nocase?: InputMaybe; 145 | readonly tokenURI_starts_with?: InputMaybe; 146 | readonly tokenURI_starts_with_nocase?: InputMaybe; 147 | }; 148 | 149 | export enum Token_OrderBy { 150 | Id = 'id', 151 | Owner = 'owner', 152 | TokenUri = 'tokenURI' 153 | } 154 | 155 | export type _Block_ = { 156 | readonly __typename?: '_Block_'; 157 | /** The hash of the block */ 158 | readonly hash?: Maybe; 159 | /** The block number */ 160 | readonly number: Scalars['Int']; 161 | /** Integer representation of the timestamp stored in blocks for the chain */ 162 | readonly timestamp?: Maybe; 163 | }; 164 | 165 | /** The type for the top-level _meta field */ 166 | export type _Meta_ = { 167 | readonly __typename?: '_Meta_'; 168 | /** 169 | * Information about a specific subgraph block. The hash of the block 170 | * will be null if the _meta field has a block constraint that asks for 171 | * a block number. It will be filled if the _meta field has no block constraint 172 | * and therefore asks for the latest block 173 | * 174 | */ 175 | readonly block: _Block_; 176 | /** The deployment ID */ 177 | readonly deployment: Scalars['String']; 178 | /** If `true`, the subgraph encountered indexing errors at some past block */ 179 | readonly hasIndexingErrors: Scalars['Boolean']; 180 | }; 181 | 182 | export enum _SubgraphErrorPolicy_ { 183 | /** Data will be returned even if the subgraph has indexing errors */ 184 | Allow = 'allow', 185 | /** If the subgraph has indexing errors, data will be omitted. The default. */ 186 | Deny = 'deny' 187 | } 188 | 189 | export type InventoryQueryVariables = Exact<{ 190 | owner: Scalars['Bytes']; 191 | }>; 192 | 193 | 194 | export type InventoryQuery = { readonly __typename?: 'Query', readonly tokens: ReadonlyArray<{ readonly __typename?: 'Token', readonly id: string, readonly tokenURI: string }> }; 195 | 196 | 197 | export const InventoryDocument = gql` 198 | query Inventory($owner: Bytes!) { 199 | tokens(where: {owner: $owner}, first: 100) { 200 | id 201 | tokenURI 202 | } 203 | } 204 | `; 205 | 206 | export function useInventoryQuery(options: Omit, 'query'>) { 207 | return Urql.useQuery({ query: InventoryDocument, ...options }); 208 | }; -------------------------------------------------------------------------------- /packages/app/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /packages/app/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | module.exports = { 3 | reactStrictMode: true, 4 | }; 5 | -------------------------------------------------------------------------------- /packages/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web3-scaffold/app", 3 | "private": true, 4 | "engines": { 5 | "node": "16", 6 | "pnpm": "7" 7 | }, 8 | "scripts": { 9 | "dev": "next dev & graphql-codegen --watch", 10 | "build": "next build", 11 | "start": "next start", 12 | "lint": "eslint .", 13 | "codegen": "graphql-codegen", 14 | "prettier": "prettier --write src" 15 | }, 16 | "dependencies": { 17 | "@ethersproject/address": "5.7.0", 18 | "@ethersproject/contracts": "5.7.0", 19 | "@ethersproject/providers": "5.7.2", 20 | "@rainbow-me/rainbowkit": "0.8.0", 21 | "@web3-scaffold/contracts": "workspace:*", 22 | "classnames": "^2.3.1", 23 | "ethers": "5.7.2", 24 | "graphql": "^16.6.0", 25 | "next": "12.3.0", 26 | "react": "18.2.0", 27 | "react-dom": "18.2.0", 28 | "react-toastify": "^9.1.1", 29 | "urql": "^2.2.3", 30 | "wagmi": "0.8.10", 31 | "zustand": "^3.7.2" 32 | }, 33 | "devDependencies": { 34 | "@graphql-codegen/cli": "^2.12.0", 35 | "@graphql-codegen/typescript": "^2.7.3", 36 | "@graphql-codegen/typescript-operations": "^2.5.3", 37 | "@graphql-codegen/typescript-urql": "^3.7.0", 38 | "@types/node": "17.0.0", 39 | "@types/react": "^18.0.26", 40 | "autoprefixer": "^10.4.13", 41 | "eslint-config-next": "^12.1.6", 42 | "eslint-plugin-react-hooks": "^4.6.0", 43 | "postcss": "^8.4.19", 44 | "tailwindcss": "^3.2.4", 45 | "typescript": "^4.5.5" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/app/public/robots.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frolic/web3-scaffold/7a1767b499b97e6b49de7535e6cc39cb4d8daf87/packages/app/public/robots.txt -------------------------------------------------------------------------------- /packages/app/src/Button.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import { ButtonHTMLAttributes, DetailedHTMLProps } from "react"; 3 | 4 | import { PendingIcon } from "./PendingIcon"; 5 | 6 | const buttonClasses = 7 | "self-center transition text-white bg-sky-600 hover:bg-cyan-600 active:bg-cyan-700 disabled:bg-slate-400 px-6 py-3 rounded-lg text-xl flex"; 8 | 9 | type Props = { 10 | children: React.ReactNode; 11 | pending?: boolean; 12 | }; 13 | 14 | type ButtonProps = Props & 15 | DetailedHTMLProps, HTMLButtonElement>; 16 | 17 | export const Button = ({ 18 | pending, 19 | type, 20 | className, 21 | children, 22 | disabled, 23 | ...props 24 | }: ButtonProps) => { 25 | return ( 26 | 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /packages/app/src/EthereumProviders.tsx: -------------------------------------------------------------------------------- 1 | import "@rainbow-me/rainbowkit/styles.css"; 2 | 3 | import { getDefaultWallets, RainbowKitProvider } from "@rainbow-me/rainbowkit"; 4 | import { 5 | allChains, 6 | chain, 7 | configureChains, 8 | createClient, 9 | WagmiConfig, 10 | } from "wagmi"; 11 | import { alchemyProvider } from "wagmi/providers/alchemy"; 12 | import { publicProvider } from "wagmi/providers/public"; 13 | 14 | // Will default to goerli if nothing set in the ENV 15 | export const targetChainId = 16 | parseInt(process.env.NEXT_PUBLIC_CHAIN_ID || "0") || 5; 17 | 18 | export const targetChain = (() => { 19 | const c = allChains.find((c) => c.id === targetChainId); 20 | if (!c) { 21 | throw new Error(`No chain config found for chain ID ${targetChainId}`); 22 | } 23 | return c; 24 | })(); 25 | 26 | // filter down to just mainnet + optional target testnet chain so that rainbowkit can tell 27 | // the user to switch network if they're on an alternative one 28 | const targetChains = [targetChain, chain.mainnet]; 29 | 30 | export const { chains, provider, webSocketProvider } = configureChains( 31 | targetChains, 32 | [ 33 | alchemyProvider({ apiKey: process.env.NEXT_PUBLIC_ALCHEMY_API_KEY! }), 34 | publicProvider(), 35 | ] 36 | ); 37 | 38 | const { connectors } = getDefaultWallets({ 39 | appName: "Example NFT", 40 | chains, 41 | }); 42 | 43 | export const wagmiClient = createClient({ 44 | autoConnect: true, 45 | connectors, 46 | provider, 47 | webSocketProvider, 48 | }); 49 | 50 | type Props = { 51 | children: React.ReactNode; 52 | }; 53 | 54 | export const EthereumProviders = ({ children }: Props) => ( 55 | 56 | {children} 57 | 58 | ); 59 | -------------------------------------------------------------------------------- /packages/app/src/Inventory.tsx: -------------------------------------------------------------------------------- 1 | import { gql } from "urql"; 2 | import { useAccount } from "wagmi"; 3 | 4 | import { useInventoryQuery } from "../codegen/subgraph"; 5 | import { exampleNFTContract } from "./contracts"; 6 | import { PendingIcon } from "./PendingIcon"; 7 | import { useIsMounted } from "./useIsMounted"; 8 | 9 | gql` 10 | query Inventory($owner: Bytes!) { 11 | tokens(where: { owner: $owner }, first: 100) { 12 | id 13 | tokenURI 14 | } 15 | } 16 | `; 17 | 18 | export const Inventory = () => { 19 | const { address } = useAccount(); 20 | 21 | const [query] = useInventoryQuery({ 22 | pause: !address, 23 | variables: { 24 | owner: address?.toLowerCase(), 25 | }, 26 | }); 27 | 28 | // Temporarily workaround hydration issues where server-rendered markup 29 | // doesn't match the client due to localStorage caching in wagmi 30 | // See https://github.com/holic/web3-scaffold/pull/26 31 | const isMounted = useIsMounted(); 32 | if (!isMounted) { 33 | return null; 34 | } 35 | 36 | if (!address) { 37 | return null; 38 | } 39 | 40 | if (!query.data) { 41 | return ; 42 | } 43 | 44 | return ( 45 |
46 |
47 | Inventory 48 |
49 |
50 | {query.data.tokens.map((token) => ( 51 | 58 | Token #{token.id} 59 | 60 | ))} 61 |
62 |
63 | ); 64 | }; 65 | -------------------------------------------------------------------------------- /packages/app/src/MintButton.tsx: -------------------------------------------------------------------------------- 1 | import { toast } from "react-toastify"; 2 | import { useAccount } from "wagmi"; 3 | 4 | import { Button } from "./Button"; 5 | import { exampleNFTContract } from "./contracts"; 6 | import { extractContractError } from "./extractContractError"; 7 | import { pluralize } from "./pluralize"; 8 | import { promiseNotify } from "./promiseNotify"; 9 | import { switchChain } from "./switchChain"; 10 | import { usePromiseFn } from "./usePromiseFn"; 11 | 12 | export const MintButton = () => { 13 | const { connector } = useAccount(); 14 | 15 | const [mintResult, mint] = usePromiseFn( 16 | async (quantity: number, onProgress: (message: string) => void) => { 17 | if (!connector) { 18 | throw new Error("Wallet not connected"); 19 | } 20 | 21 | onProgress("Preparing wallet…"); 22 | await switchChain(connector); 23 | const signer = await connector.getSigner(); 24 | const contract = exampleNFTContract.connect(signer); 25 | const price = await contract.price(); 26 | 27 | try { 28 | onProgress(`Minting ${pluralize(quantity, "token", "tokens")}…`); 29 | 30 | const tx = await promiseNotify( 31 | contract.mint(quantity, { value: price.mul(quantity) }) 32 | ).after(1000 * 5, () => 33 | onProgress("Please confirm transaction in your wallet…") 34 | ); 35 | console.log("mint tx", tx); 36 | 37 | onProgress("Finalizing transaction…"); 38 | const receipt = await promiseNotify(tx.wait()) 39 | .after(1000 * 15, () => 40 | onProgress( 41 | "It can sometimes take a while to finalize a transaction…" 42 | ) 43 | ) 44 | .after(1000 * 30, () => onProgress("Still working on it…")); 45 | console.log("mint receipt", receipt); 46 | 47 | return { receipt }; 48 | } catch (error) { 49 | console.error("Transaction error:", error); 50 | const contractError = extractContractError(error); 51 | throw new Error(`Transaction error: ${contractError}`); 52 | } 53 | }, 54 | [connector] 55 | ); 56 | 57 | return ( 58 | 90 | ); 91 | }; 92 | -------------------------------------------------------------------------------- /packages/app/src/PendingIcon.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | 3 | export const PendingIcon = ({ className }: { className?: string }) => ( 4 | 10 | 18 | 23 | 24 | ); 25 | -------------------------------------------------------------------------------- /packages/app/src/cachedFetch.ts: -------------------------------------------------------------------------------- 1 | const cache: Partial>> = {}; 2 | 3 | export const cachedFetch = async ( 4 | ...args: ConstructorParameters 5 | ) => { 6 | const req = new Request(...args); 7 | if (req.method !== "GET") { 8 | throw new Error("cachedFetch does not support methods other than GET"); 9 | } 10 | 11 | const key = req.url; 12 | const cached = cache[key]; 13 | if (cached) return cached.then((res) => res.clone()); 14 | 15 | const res = fetch(req); 16 | cache[key] = res; 17 | 18 | // unset cache if fetch fails 19 | res.catch(() => delete cache[key]); 20 | 21 | return res.then((res) => res.clone()); 22 | }; 23 | -------------------------------------------------------------------------------- /packages/app/src/contracts.ts: -------------------------------------------------------------------------------- 1 | import ExampleNFTGoerli from "@web3-scaffold/contracts/deploys/goerli/ExampleNFT.json"; 2 | import { ExampleNFT__factory } from "@web3-scaffold/contracts/types"; 3 | 4 | import { provider, targetChainId } from "./EthereumProviders"; 5 | 6 | // I would have used `ExampleNFT__factory.connect` to create this, but we may 7 | // not have a provider ready to go. Any interactions with this contract should 8 | // use `exampleNFTContract.connect(providerOrSigner)` first. 9 | 10 | // export const exampleNFTContract = new Contract( 11 | // ExampleNFTGoerli.deployedTo, 12 | // ExampleNFT__factory.abi 13 | // ) as ExampleNFT; 14 | 15 | export const exampleNFTContract = ExampleNFT__factory.connect( 16 | ExampleNFTGoerli.deployedTo, 17 | provider({ chainId: targetChainId }) 18 | ); 19 | -------------------------------------------------------------------------------- /packages/app/src/delay.ts: -------------------------------------------------------------------------------- 1 | export const delay = (ms: number) => 2 | new Promise((resolve) => setTimeout(resolve, ms)); 3 | 4 | export const delayValue = (ms: number, value: T) => 5 | new Promise((resolve) => setTimeout(() => resolve(value), ms)); 6 | -------------------------------------------------------------------------------- /packages/app/src/extractContractError.ts: -------------------------------------------------------------------------------- 1 | import { ExampleNFT__factory } from "@web3-scaffold/contracts/types"; 2 | 3 | const contractInterface = ExampleNFT__factory.createInterface(); 4 | 5 | // for (const [error, errorFragment] of Object.entries(contractInterface.errors)) { 6 | // console.log(contractInterface.getSighash(errorFragment), errorFragment.name); 7 | // } 8 | 9 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 10 | export const extractContractError = (error: any): string => { 11 | console.dir(error); 12 | 13 | // Attempt to extract Solidity error 14 | const errorData = error.error?.data?.originalError?.data; 15 | if (typeof errorData === "string") { 16 | console.log("found error data in original error, write call?", errorData); 17 | for (const [, errorFragment] of Object.entries(contractInterface.errors)) { 18 | if (errorData.startsWith(contractInterface.getSighash(errorFragment))) { 19 | // const args = contractInterface.decodeErrorResult(errorFragment, errorData) 20 | return errorFragment.name; 21 | } 22 | } 23 | } 24 | 25 | // Read calls will revert differently 26 | try { 27 | const response = JSON.parse(error.error.response); 28 | const errorData = response.error.data; 29 | console.log("found error data in error response, read call?", errorData); 30 | if (typeof errorData === "string") { 31 | for (const [, errorFragment] of Object.entries( 32 | contractInterface.errors 33 | )) { 34 | if (errorData.startsWith(contractInterface.getSighash(errorFragment))) { 35 | // const args = contractInterface.decodeErrorResult(errorFragment, errorData) 36 | return errorFragment.name; 37 | } 38 | } 39 | } 40 | } catch (error) { 41 | // do nothing with the parse error so we can continue on 42 | } 43 | 44 | // Otherwise return error reason 45 | if (typeof error.reason === "string") { 46 | return error.reason; 47 | } 48 | // Fall back to error message 49 | return error.message; 50 | }; 51 | -------------------------------------------------------------------------------- /packages/app/src/firstParam.ts: -------------------------------------------------------------------------------- 1 | export const firstParam = (param: string | string[] | undefined) => { 2 | return Array.isArray(param) ? param[0] : param; 3 | }; 4 | -------------------------------------------------------------------------------- /packages/app/src/formatAddress.ts: -------------------------------------------------------------------------------- 1 | import { getAddress } from "@ethersproject/address"; 2 | 3 | export const formatAddress = (address: string) => { 4 | try { 5 | return getAddress(address); 6 | } catch (error) { 7 | return address; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /packages/app/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "tailwindcss/tailwind.css"; 2 | import "react-toastify/dist/ReactToastify.css"; 3 | 4 | import type { AppProps } from "next/app"; 5 | import Head from "next/head"; 6 | import { ToastContainer } from "react-toastify"; 7 | import { 8 | createClient as createGraphClient, 9 | Provider as GraphProvider, 10 | } from "urql"; 11 | 12 | import { EthereumProviders } from "../EthereumProviders"; 13 | 14 | export const graphClient = createGraphClient({ 15 | url: "https://api.thegraph.com/subgraphs/name/holic/example-nft", 16 | }); 17 | 18 | const MyApp = ({ Component, pageProps }: AppProps) => { 19 | return ( 20 | <> 21 | 22 | Example NFT 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | ); 32 | }; 33 | 34 | export default MyApp; 35 | -------------------------------------------------------------------------------- /packages/app/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { ConnectButton } from "@rainbow-me/rainbowkit"; 2 | import ExampleNFTGoerli from "@web3-scaffold/contracts/deploys/goerli/ExampleNFT.json"; 3 | import { ExampleNFT__factory } from "@web3-scaffold/contracts/types"; 4 | import type { NextPage } from "next"; 5 | import { useContractRead } from "wagmi"; 6 | 7 | import { targetChainId } from "../EthereumProviders"; 8 | import { Inventory } from "../Inventory"; 9 | import { MintButton } from "../MintButton"; 10 | import { useIsMounted } from "../useIsMounted"; 11 | 12 | const HomePage: NextPage = () => { 13 | const totalSupply = useContractRead({ 14 | chainId: targetChainId, 15 | address: ExampleNFTGoerli.deployedTo, 16 | abi: ExampleNFT__factory.abi, 17 | functionName: "totalSupply", 18 | watch: true, 19 | }); 20 | 21 | const maxSupply = useContractRead({ 22 | chainId: targetChainId, 23 | address: ExampleNFTGoerli.deployedTo, 24 | abi: ExampleNFT__factory.abi, 25 | functionName: "maxSupply", 26 | }); 27 | 28 | const isMounted = useIsMounted(); 29 | 30 | return ( 31 |
32 |
33 | 34 |
35 |
36 |

Example NFT

37 | 38 | {/* Use isMounted to temporarily workaround hydration issues where 39 | server-rendered markup doesn't match the client due to localStorage 40 | caching in wagmi. See https://github.com/holic/web3-scaffold/pull/26 */} 41 |

42 | {(isMounted ? totalSupply.data?.toNumber().toLocaleString() : null) ?? 43 | "??"} 44 | / 45 | {(isMounted ? maxSupply.data?.toNumber().toLocaleString() : null) ?? 46 | "??"}{" "} 47 | minted 48 |

49 | 50 | 51 | 52 |
53 |
54 | ); 55 | }; 56 | 57 | export default HomePage; 58 | -------------------------------------------------------------------------------- /packages/app/src/pluralize.ts: -------------------------------------------------------------------------------- 1 | const pluralRules = new Intl.PluralRules("en-US"); 2 | 3 | export const pluralize = (count: number, singular: string, plural: string) => { 4 | const rule = pluralRules.select(count); 5 | switch (rule) { 6 | case "one": 7 | return `${count} ${singular}`; 8 | case "other": 9 | return `${count} ${plural}`; 10 | default: 11 | throw new Error(`Unsupported plural rule: ${rule}`); 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/app/src/promiseNotify.ts: -------------------------------------------------------------------------------- 1 | import { delayValue } from "./delay"; 2 | 3 | // For promises that take a long time (e.g. transaction confirmations), 4 | // this helps us update loading indicators (e.g. toasts) to give users 5 | // more feedback about what's going on. 6 | 7 | export class PromiseNotifier extends Promise { 8 | static wrap(promise: PromiseLike) { 9 | return new PromiseNotifier((resolve, reject) => 10 | promise.then(resolve, reject) 11 | ); 12 | } 13 | 14 | after(ms: number, fn: () => void) { 15 | const delayed = {}; 16 | return PromiseNotifier.wrap( 17 | Promise.race([this, delayValue(ms, delayed)]).then((winner) => { 18 | if (winner === delayed) { 19 | fn(); 20 | } 21 | return this; 22 | }) 23 | ); 24 | } 25 | } 26 | 27 | export const promiseNotify = (promise: PromiseLike) => 28 | PromiseNotifier.wrap(promise); 29 | -------------------------------------------------------------------------------- /packages/app/src/switchChain.ts: -------------------------------------------------------------------------------- 1 | import { Connector } from "wagmi"; 2 | 3 | import { targetChainId } from "./EthereumProviders"; 4 | 5 | // TODO: custom errors? 6 | 7 | export const switchChain = async ( 8 | connector: Connector, 9 | chainId: number = targetChainId 10 | ) => { 11 | if (!connector) { 12 | throw new Error("No wallet connected"); 13 | } 14 | 15 | const provider = await connector.getProvider(); 16 | 17 | const clientName = provider.connector?.peerMeta?.name ?? ""; 18 | const isRainbow = /rainbow/i.test(clientName); 19 | 20 | const currentChainId = await connector.getChainId(); 21 | if (currentChainId === chainId) { 22 | return; 23 | } 24 | 25 | // Triggering a network switch with Rainbow for a non-mainnet chain will get the 26 | // Rainbow app to open, but does nothing except invisibly throw an error, which you 27 | // won't see unless you tab back to the page/browser. So we'll skip it for now. 28 | // TODO: file an issue/repro case about this 29 | if (connector.switchChain && (!isRainbow || chainId === 1)) { 30 | await connector.switchChain(chainId); 31 | return; 32 | } 33 | 34 | // Likely the connected wallet doesn't support chain switching 35 | throw new Error("Wrong network"); 36 | }; 37 | -------------------------------------------------------------------------------- /packages/app/src/useENS.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import createStore from "zustand"; 3 | import { persist } from "zustand/middleware"; 4 | 5 | import { cachedFetch } from "./cachedFetch"; 6 | 7 | type State = { 8 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 9 | resolvedAddresses: Partial>; 10 | }; 11 | 12 | export const useStore = createStore( 13 | persist(() => ({ resolvedAddresses: {} }), { name: "resolved-ens" }) 14 | ); 15 | 16 | export const useENS = (address: string) => { 17 | const addressLowercase = address.toLowerCase(); 18 | const resolved = useStore( 19 | (state) => state.resolvedAddresses[addressLowercase] 20 | ); 21 | 22 | useEffect(() => { 23 | (async () => { 24 | try { 25 | const data = await cachedFetch( 26 | `https://api.ensideas.com/ens/resolve/${addressLowercase}` 27 | ).then((res) => res.json()); 28 | useStore.setState((state) => ({ 29 | resolvedAddresses: { 30 | ...state.resolvedAddresses, 31 | [addressLowercase]: data, 32 | }, 33 | })); 34 | } catch (error) { 35 | console.log("could not resolve ens", error); 36 | } 37 | })(); 38 | }, [addressLowercase]); 39 | 40 | return { 41 | address: resolved?.address, 42 | name: resolved?.name, 43 | displayName: resolved?.displayName, 44 | avatar: resolved?.avatar, 45 | }; 46 | }; 47 | -------------------------------------------------------------------------------- /packages/app/src/useIsMounted.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | export function useIsMounted() { 4 | const [mounted, setMounted] = useState(false); 5 | 6 | useEffect(() => setMounted(true), []); 7 | 8 | return mounted; 9 | } 10 | -------------------------------------------------------------------------------- /packages/app/src/usePromise.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | type PromiseState = 4 | | { type: "pending"; value: TValue | undefined } 5 | | { type: "fulfilled"; value: TValue } 6 | | { type: "rejected"; error: TError }; 7 | 8 | export const usePromise = ( 9 | promise: PromiseLike, 10 | initialValue?: TValue 11 | ) => { 12 | const [state, setState] = useState>({ 13 | type: "pending", 14 | value: initialValue, 15 | }); 16 | 17 | useEffect(() => { 18 | let mounted = true; 19 | 20 | promise.then( 21 | (value) => mounted && setState({ type: "fulfilled", value }), 22 | (error) => mounted && setState({ type: "rejected", error }) 23 | ); 24 | 25 | return () => { 26 | mounted = false; 27 | }; 28 | }, [promise]); 29 | 30 | return state; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/app/src/usePromiseFn.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DependencyList, 3 | useCallback, 4 | useEffect, 5 | useRef, 6 | useState, 7 | } from "react"; 8 | 9 | type PromiseState = 10 | | { type: "idle"; value: TValue | undefined } 11 | | { type: "pending"; value: TValue | undefined } 12 | | { type: "fulfilled"; value: TValue } 13 | | { type: "rejected"; error: unknown }; 14 | 15 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 16 | type AsyncFunction = (...args: any[]) => PromiseLike; 17 | 18 | export const usePromiseFn = ( 19 | promiseFn: TFunc, 20 | deps: DependencyList = [] 21 | ): [ 22 | PromiseState>>, 23 | (...args: Parameters) => ReturnType 24 | ] => { 25 | const mounted = useRef(false); 26 | const lastPromiseId = useRef(0); 27 | const [state, setState] = useState>>>({ 28 | type: "idle", 29 | value: undefined, 30 | }); 31 | 32 | useEffect(() => { 33 | mounted.current = true; 34 | return () => { 35 | mounted.current = false; 36 | }; 37 | }, []); 38 | 39 | const currentValue = state.type === "rejected" ? undefined : state.value; 40 | 41 | const mutate = useCallback( 42 | (...args: Parameters) => { 43 | if (!mounted.current) { 44 | throw new Error( 45 | "usePromiseFn: tried to call promise fn while not mounted" 46 | ); 47 | } 48 | 49 | setState({ 50 | type: "pending", 51 | value: currentValue, 52 | }); 53 | const promiseId = ++lastPromiseId.current; 54 | const promise = promiseFn(...args) as ReturnType; 55 | promise.then( 56 | (value) => 57 | mounted.current && 58 | promiseId === lastPromiseId.current && 59 | setState({ type: "fulfilled", value }), 60 | (error) => 61 | mounted.current && 62 | promiseId === lastPromiseId.current && 63 | setState({ type: "rejected", error }) 64 | ); 65 | return promise; 66 | }, 67 | // eslint-disable-next-line react-hooks/exhaustive-deps 68 | [...deps, currentValue] 69 | ); 70 | 71 | return [state, mutate]; 72 | }; 73 | -------------------------------------------------------------------------------- /packages/app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/contracts/.env: -------------------------------------------------------------------------------- 1 | DEPLOYER= 2 | DEPLOYER_PRIVATE_KEY= 3 | CHAIN_NAME=goerli 4 | RPC_URL= 5 | ETHERSCAN_API_KEY= 6 | -------------------------------------------------------------------------------- /packages/contracts/.gitignore: -------------------------------------------------------------------------------- 1 | .env.* 2 | 3 | # forge output 4 | cache 5 | out 6 | broadcast 7 | 8 | abi/**/*.json 9 | !abi/**/*.abi.json 10 | -------------------------------------------------------------------------------- /packages/contracts/abi/Context.sol/Context.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /packages/contracts/abi/ERC721A.sol/ERC721A.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "string", 6 | "name": "name_", 7 | "type": "string" 8 | }, 9 | { 10 | "internalType": "string", 11 | "name": "symbol_", 12 | "type": "string" 13 | } 14 | ], 15 | "stateMutability": "nonpayable", 16 | "type": "constructor" 17 | }, 18 | { 19 | "inputs": [], 20 | "name": "ApprovalCallerNotOwnerNorApproved", 21 | "type": "error" 22 | }, 23 | { 24 | "inputs": [], 25 | "name": "ApprovalQueryForNonexistentToken", 26 | "type": "error" 27 | }, 28 | { 29 | "inputs": [], 30 | "name": "ApproveToCaller", 31 | "type": "error" 32 | }, 33 | { 34 | "inputs": [], 35 | "name": "BalanceQueryForZeroAddress", 36 | "type": "error" 37 | }, 38 | { 39 | "inputs": [], 40 | "name": "MintToZeroAddress", 41 | "type": "error" 42 | }, 43 | { 44 | "inputs": [], 45 | "name": "MintZeroQuantity", 46 | "type": "error" 47 | }, 48 | { 49 | "inputs": [], 50 | "name": "OwnerQueryForNonexistentToken", 51 | "type": "error" 52 | }, 53 | { 54 | "inputs": [], 55 | "name": "TransferCallerNotOwnerNorApproved", 56 | "type": "error" 57 | }, 58 | { 59 | "inputs": [], 60 | "name": "TransferFromIncorrectOwner", 61 | "type": "error" 62 | }, 63 | { 64 | "inputs": [], 65 | "name": "TransferToNonERC721ReceiverImplementer", 66 | "type": "error" 67 | }, 68 | { 69 | "inputs": [], 70 | "name": "TransferToZeroAddress", 71 | "type": "error" 72 | }, 73 | { 74 | "inputs": [], 75 | "name": "URIQueryForNonexistentToken", 76 | "type": "error" 77 | }, 78 | { 79 | "anonymous": false, 80 | "inputs": [ 81 | { 82 | "indexed": true, 83 | "internalType": "address", 84 | "name": "owner", 85 | "type": "address" 86 | }, 87 | { 88 | "indexed": true, 89 | "internalType": "address", 90 | "name": "approved", 91 | "type": "address" 92 | }, 93 | { 94 | "indexed": true, 95 | "internalType": "uint256", 96 | "name": "tokenId", 97 | "type": "uint256" 98 | } 99 | ], 100 | "name": "Approval", 101 | "type": "event" 102 | }, 103 | { 104 | "anonymous": false, 105 | "inputs": [ 106 | { 107 | "indexed": true, 108 | "internalType": "address", 109 | "name": "owner", 110 | "type": "address" 111 | }, 112 | { 113 | "indexed": true, 114 | "internalType": "address", 115 | "name": "operator", 116 | "type": "address" 117 | }, 118 | { 119 | "indexed": false, 120 | "internalType": "bool", 121 | "name": "approved", 122 | "type": "bool" 123 | } 124 | ], 125 | "name": "ApprovalForAll", 126 | "type": "event" 127 | }, 128 | { 129 | "anonymous": false, 130 | "inputs": [ 131 | { 132 | "indexed": true, 133 | "internalType": "address", 134 | "name": "from", 135 | "type": "address" 136 | }, 137 | { 138 | "indexed": true, 139 | "internalType": "address", 140 | "name": "to", 141 | "type": "address" 142 | }, 143 | { 144 | "indexed": true, 145 | "internalType": "uint256", 146 | "name": "tokenId", 147 | "type": "uint256" 148 | } 149 | ], 150 | "name": "Transfer", 151 | "type": "event" 152 | }, 153 | { 154 | "inputs": [ 155 | { 156 | "internalType": "address", 157 | "name": "to", 158 | "type": "address" 159 | }, 160 | { 161 | "internalType": "uint256", 162 | "name": "tokenId", 163 | "type": "uint256" 164 | } 165 | ], 166 | "name": "approve", 167 | "outputs": [], 168 | "stateMutability": "nonpayable", 169 | "type": "function" 170 | }, 171 | { 172 | "inputs": [ 173 | { 174 | "internalType": "address", 175 | "name": "owner", 176 | "type": "address" 177 | } 178 | ], 179 | "name": "balanceOf", 180 | "outputs": [ 181 | { 182 | "internalType": "uint256", 183 | "name": "", 184 | "type": "uint256" 185 | } 186 | ], 187 | "stateMutability": "view", 188 | "type": "function" 189 | }, 190 | { 191 | "inputs": [ 192 | { 193 | "internalType": "uint256", 194 | "name": "tokenId", 195 | "type": "uint256" 196 | } 197 | ], 198 | "name": "getApproved", 199 | "outputs": [ 200 | { 201 | "internalType": "address", 202 | "name": "", 203 | "type": "address" 204 | } 205 | ], 206 | "stateMutability": "view", 207 | "type": "function" 208 | }, 209 | { 210 | "inputs": [ 211 | { 212 | "internalType": "address", 213 | "name": "owner", 214 | "type": "address" 215 | }, 216 | { 217 | "internalType": "address", 218 | "name": "operator", 219 | "type": "address" 220 | } 221 | ], 222 | "name": "isApprovedForAll", 223 | "outputs": [ 224 | { 225 | "internalType": "bool", 226 | "name": "", 227 | "type": "bool" 228 | } 229 | ], 230 | "stateMutability": "view", 231 | "type": "function" 232 | }, 233 | { 234 | "inputs": [], 235 | "name": "name", 236 | "outputs": [ 237 | { 238 | "internalType": "string", 239 | "name": "", 240 | "type": "string" 241 | } 242 | ], 243 | "stateMutability": "view", 244 | "type": "function" 245 | }, 246 | { 247 | "inputs": [ 248 | { 249 | "internalType": "uint256", 250 | "name": "tokenId", 251 | "type": "uint256" 252 | } 253 | ], 254 | "name": "ownerOf", 255 | "outputs": [ 256 | { 257 | "internalType": "address", 258 | "name": "", 259 | "type": "address" 260 | } 261 | ], 262 | "stateMutability": "view", 263 | "type": "function" 264 | }, 265 | { 266 | "inputs": [ 267 | { 268 | "internalType": "address", 269 | "name": "from", 270 | "type": "address" 271 | }, 272 | { 273 | "internalType": "address", 274 | "name": "to", 275 | "type": "address" 276 | }, 277 | { 278 | "internalType": "uint256", 279 | "name": "tokenId", 280 | "type": "uint256" 281 | } 282 | ], 283 | "name": "safeTransferFrom", 284 | "outputs": [], 285 | "stateMutability": "nonpayable", 286 | "type": "function" 287 | }, 288 | { 289 | "inputs": [ 290 | { 291 | "internalType": "address", 292 | "name": "from", 293 | "type": "address" 294 | }, 295 | { 296 | "internalType": "address", 297 | "name": "to", 298 | "type": "address" 299 | }, 300 | { 301 | "internalType": "uint256", 302 | "name": "tokenId", 303 | "type": "uint256" 304 | }, 305 | { 306 | "internalType": "bytes", 307 | "name": "_data", 308 | "type": "bytes" 309 | } 310 | ], 311 | "name": "safeTransferFrom", 312 | "outputs": [], 313 | "stateMutability": "nonpayable", 314 | "type": "function" 315 | }, 316 | { 317 | "inputs": [ 318 | { 319 | "internalType": "address", 320 | "name": "operator", 321 | "type": "address" 322 | }, 323 | { 324 | "internalType": "bool", 325 | "name": "approved", 326 | "type": "bool" 327 | } 328 | ], 329 | "name": "setApprovalForAll", 330 | "outputs": [], 331 | "stateMutability": "nonpayable", 332 | "type": "function" 333 | }, 334 | { 335 | "inputs": [ 336 | { 337 | "internalType": "bytes4", 338 | "name": "interfaceId", 339 | "type": "bytes4" 340 | } 341 | ], 342 | "name": "supportsInterface", 343 | "outputs": [ 344 | { 345 | "internalType": "bool", 346 | "name": "", 347 | "type": "bool" 348 | } 349 | ], 350 | "stateMutability": "view", 351 | "type": "function" 352 | }, 353 | { 354 | "inputs": [], 355 | "name": "symbol", 356 | "outputs": [ 357 | { 358 | "internalType": "string", 359 | "name": "", 360 | "type": "string" 361 | } 362 | ], 363 | "stateMutability": "view", 364 | "type": "function" 365 | }, 366 | { 367 | "inputs": [ 368 | { 369 | "internalType": "uint256", 370 | "name": "tokenId", 371 | "type": "uint256" 372 | } 373 | ], 374 | "name": "tokenURI", 375 | "outputs": [ 376 | { 377 | "internalType": "string", 378 | "name": "", 379 | "type": "string" 380 | } 381 | ], 382 | "stateMutability": "view", 383 | "type": "function" 384 | }, 385 | { 386 | "inputs": [], 387 | "name": "totalSupply", 388 | "outputs": [ 389 | { 390 | "internalType": "uint256", 391 | "name": "", 392 | "type": "uint256" 393 | } 394 | ], 395 | "stateMutability": "view", 396 | "type": "function" 397 | }, 398 | { 399 | "inputs": [ 400 | { 401 | "internalType": "address", 402 | "name": "from", 403 | "type": "address" 404 | }, 405 | { 406 | "internalType": "address", 407 | "name": "to", 408 | "type": "address" 409 | }, 410 | { 411 | "internalType": "uint256", 412 | "name": "tokenId", 413 | "type": "uint256" 414 | } 415 | ], 416 | "name": "transferFrom", 417 | "outputs": [], 418 | "stateMutability": "nonpayable", 419 | "type": "function" 420 | } 421 | ] -------------------------------------------------------------------------------- /packages/contracts/abi/ERC721A.sol/ERC721A__IERC721Receiver.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "operator", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "from", 12 | "type": "address" 13 | }, 14 | { 15 | "internalType": "uint256", 16 | "name": "tokenId", 17 | "type": "uint256" 18 | }, 19 | { 20 | "internalType": "bytes", 21 | "name": "data", 22 | "type": "bytes" 23 | } 24 | ], 25 | "name": "onERC721Received", 26 | "outputs": [ 27 | { 28 | "internalType": "bytes4", 29 | "name": "", 30 | "type": "bytes4" 31 | } 32 | ], 33 | "stateMutability": "nonpayable", 34 | "type": "function" 35 | } 36 | ] -------------------------------------------------------------------------------- /packages/contracts/abi/IERC165.sol/IERC165.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes4", 6 | "name": "interfaceId", 7 | "type": "bytes4" 8 | } 9 | ], 10 | "name": "supportsInterface", 11 | "outputs": [ 12 | { 13 | "internalType": "bool", 14 | "name": "", 15 | "type": "bool" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | } 21 | ] -------------------------------------------------------------------------------- /packages/contracts/abi/IERC20.sol/IERC20.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "owner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "spender", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "value", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Approval", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "from", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "to", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "value", 46 | "type": "uint256" 47 | } 48 | ], 49 | "name": "Transfer", 50 | "type": "event" 51 | }, 52 | { 53 | "inputs": [ 54 | { 55 | "internalType": "address", 56 | "name": "owner", 57 | "type": "address" 58 | }, 59 | { 60 | "internalType": "address", 61 | "name": "spender", 62 | "type": "address" 63 | } 64 | ], 65 | "name": "allowance", 66 | "outputs": [ 67 | { 68 | "internalType": "uint256", 69 | "name": "", 70 | "type": "uint256" 71 | } 72 | ], 73 | "stateMutability": "view", 74 | "type": "function" 75 | }, 76 | { 77 | "inputs": [ 78 | { 79 | "internalType": "address", 80 | "name": "spender", 81 | "type": "address" 82 | }, 83 | { 84 | "internalType": "uint256", 85 | "name": "amount", 86 | "type": "uint256" 87 | } 88 | ], 89 | "name": "approve", 90 | "outputs": [ 91 | { 92 | "internalType": "bool", 93 | "name": "", 94 | "type": "bool" 95 | } 96 | ], 97 | "stateMutability": "nonpayable", 98 | "type": "function" 99 | }, 100 | { 101 | "inputs": [ 102 | { 103 | "internalType": "address", 104 | "name": "account", 105 | "type": "address" 106 | } 107 | ], 108 | "name": "balanceOf", 109 | "outputs": [ 110 | { 111 | "internalType": "uint256", 112 | "name": "", 113 | "type": "uint256" 114 | } 115 | ], 116 | "stateMutability": "view", 117 | "type": "function" 118 | }, 119 | { 120 | "inputs": [], 121 | "name": "totalSupply", 122 | "outputs": [ 123 | { 124 | "internalType": "uint256", 125 | "name": "", 126 | "type": "uint256" 127 | } 128 | ], 129 | "stateMutability": "view", 130 | "type": "function" 131 | }, 132 | { 133 | "inputs": [ 134 | { 135 | "internalType": "address", 136 | "name": "to", 137 | "type": "address" 138 | }, 139 | { 140 | "internalType": "uint256", 141 | "name": "amount", 142 | "type": "uint256" 143 | } 144 | ], 145 | "name": "transfer", 146 | "outputs": [ 147 | { 148 | "internalType": "bool", 149 | "name": "", 150 | "type": "bool" 151 | } 152 | ], 153 | "stateMutability": "nonpayable", 154 | "type": "function" 155 | }, 156 | { 157 | "inputs": [ 158 | { 159 | "internalType": "address", 160 | "name": "from", 161 | "type": "address" 162 | }, 163 | { 164 | "internalType": "address", 165 | "name": "to", 166 | "type": "address" 167 | }, 168 | { 169 | "internalType": "uint256", 170 | "name": "amount", 171 | "type": "uint256" 172 | } 173 | ], 174 | "name": "transferFrom", 175 | "outputs": [ 176 | { 177 | "internalType": "bool", 178 | "name": "", 179 | "type": "bool" 180 | } 181 | ], 182 | "stateMutability": "nonpayable", 183 | "type": "function" 184 | } 185 | ] -------------------------------------------------------------------------------- /packages/contracts/abi/IERC2981.sol/IERC2981.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "uint256", 6 | "name": "tokenId", 7 | "type": "uint256" 8 | }, 9 | { 10 | "internalType": "uint256", 11 | "name": "salePrice", 12 | "type": "uint256" 13 | } 14 | ], 15 | "name": "royaltyInfo", 16 | "outputs": [ 17 | { 18 | "internalType": "address", 19 | "name": "receiver", 20 | "type": "address" 21 | }, 22 | { 23 | "internalType": "uint256", 24 | "name": "royaltyAmount", 25 | "type": "uint256" 26 | } 27 | ], 28 | "stateMutability": "view", 29 | "type": "function" 30 | }, 31 | { 32 | "inputs": [ 33 | { 34 | "internalType": "bytes4", 35 | "name": "interfaceId", 36 | "type": "bytes4" 37 | } 38 | ], 39 | "name": "supportsInterface", 40 | "outputs": [ 41 | { 42 | "internalType": "bool", 43 | "name": "", 44 | "type": "bool" 45 | } 46 | ], 47 | "stateMutability": "view", 48 | "type": "function" 49 | } 50 | ] -------------------------------------------------------------------------------- /packages/contracts/abi/IERC721A.sol/IERC721A.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "ApprovalCallerNotOwnerNorApproved", 5 | "type": "error" 6 | }, 7 | { 8 | "inputs": [], 9 | "name": "ApprovalQueryForNonexistentToken", 10 | "type": "error" 11 | }, 12 | { 13 | "inputs": [], 14 | "name": "ApproveToCaller", 15 | "type": "error" 16 | }, 17 | { 18 | "inputs": [], 19 | "name": "BalanceQueryForZeroAddress", 20 | "type": "error" 21 | }, 22 | { 23 | "inputs": [], 24 | "name": "MintToZeroAddress", 25 | "type": "error" 26 | }, 27 | { 28 | "inputs": [], 29 | "name": "MintZeroQuantity", 30 | "type": "error" 31 | }, 32 | { 33 | "inputs": [], 34 | "name": "OwnerQueryForNonexistentToken", 35 | "type": "error" 36 | }, 37 | { 38 | "inputs": [], 39 | "name": "TransferCallerNotOwnerNorApproved", 40 | "type": "error" 41 | }, 42 | { 43 | "inputs": [], 44 | "name": "TransferFromIncorrectOwner", 45 | "type": "error" 46 | }, 47 | { 48 | "inputs": [], 49 | "name": "TransferToNonERC721ReceiverImplementer", 50 | "type": "error" 51 | }, 52 | { 53 | "inputs": [], 54 | "name": "TransferToZeroAddress", 55 | "type": "error" 56 | }, 57 | { 58 | "inputs": [], 59 | "name": "URIQueryForNonexistentToken", 60 | "type": "error" 61 | }, 62 | { 63 | "anonymous": false, 64 | "inputs": [ 65 | { 66 | "indexed": true, 67 | "internalType": "address", 68 | "name": "owner", 69 | "type": "address" 70 | }, 71 | { 72 | "indexed": true, 73 | "internalType": "address", 74 | "name": "approved", 75 | "type": "address" 76 | }, 77 | { 78 | "indexed": true, 79 | "internalType": "uint256", 80 | "name": "tokenId", 81 | "type": "uint256" 82 | } 83 | ], 84 | "name": "Approval", 85 | "type": "event" 86 | }, 87 | { 88 | "anonymous": false, 89 | "inputs": [ 90 | { 91 | "indexed": true, 92 | "internalType": "address", 93 | "name": "owner", 94 | "type": "address" 95 | }, 96 | { 97 | "indexed": true, 98 | "internalType": "address", 99 | "name": "operator", 100 | "type": "address" 101 | }, 102 | { 103 | "indexed": false, 104 | "internalType": "bool", 105 | "name": "approved", 106 | "type": "bool" 107 | } 108 | ], 109 | "name": "ApprovalForAll", 110 | "type": "event" 111 | }, 112 | { 113 | "anonymous": false, 114 | "inputs": [ 115 | { 116 | "indexed": true, 117 | "internalType": "address", 118 | "name": "from", 119 | "type": "address" 120 | }, 121 | { 122 | "indexed": true, 123 | "internalType": "address", 124 | "name": "to", 125 | "type": "address" 126 | }, 127 | { 128 | "indexed": true, 129 | "internalType": "uint256", 130 | "name": "tokenId", 131 | "type": "uint256" 132 | } 133 | ], 134 | "name": "Transfer", 135 | "type": "event" 136 | }, 137 | { 138 | "inputs": [ 139 | { 140 | "internalType": "address", 141 | "name": "to", 142 | "type": "address" 143 | }, 144 | { 145 | "internalType": "uint256", 146 | "name": "tokenId", 147 | "type": "uint256" 148 | } 149 | ], 150 | "name": "approve", 151 | "outputs": [], 152 | "stateMutability": "nonpayable", 153 | "type": "function" 154 | }, 155 | { 156 | "inputs": [ 157 | { 158 | "internalType": "address", 159 | "name": "owner", 160 | "type": "address" 161 | } 162 | ], 163 | "name": "balanceOf", 164 | "outputs": [ 165 | { 166 | "internalType": "uint256", 167 | "name": "balance", 168 | "type": "uint256" 169 | } 170 | ], 171 | "stateMutability": "view", 172 | "type": "function" 173 | }, 174 | { 175 | "inputs": [ 176 | { 177 | "internalType": "uint256", 178 | "name": "tokenId", 179 | "type": "uint256" 180 | } 181 | ], 182 | "name": "getApproved", 183 | "outputs": [ 184 | { 185 | "internalType": "address", 186 | "name": "operator", 187 | "type": "address" 188 | } 189 | ], 190 | "stateMutability": "view", 191 | "type": "function" 192 | }, 193 | { 194 | "inputs": [ 195 | { 196 | "internalType": "address", 197 | "name": "owner", 198 | "type": "address" 199 | }, 200 | { 201 | "internalType": "address", 202 | "name": "operator", 203 | "type": "address" 204 | } 205 | ], 206 | "name": "isApprovedForAll", 207 | "outputs": [ 208 | { 209 | "internalType": "bool", 210 | "name": "", 211 | "type": "bool" 212 | } 213 | ], 214 | "stateMutability": "view", 215 | "type": "function" 216 | }, 217 | { 218 | "inputs": [], 219 | "name": "name", 220 | "outputs": [ 221 | { 222 | "internalType": "string", 223 | "name": "", 224 | "type": "string" 225 | } 226 | ], 227 | "stateMutability": "view", 228 | "type": "function" 229 | }, 230 | { 231 | "inputs": [ 232 | { 233 | "internalType": "uint256", 234 | "name": "tokenId", 235 | "type": "uint256" 236 | } 237 | ], 238 | "name": "ownerOf", 239 | "outputs": [ 240 | { 241 | "internalType": "address", 242 | "name": "owner", 243 | "type": "address" 244 | } 245 | ], 246 | "stateMutability": "view", 247 | "type": "function" 248 | }, 249 | { 250 | "inputs": [ 251 | { 252 | "internalType": "address", 253 | "name": "from", 254 | "type": "address" 255 | }, 256 | { 257 | "internalType": "address", 258 | "name": "to", 259 | "type": "address" 260 | }, 261 | { 262 | "internalType": "uint256", 263 | "name": "tokenId", 264 | "type": "uint256" 265 | } 266 | ], 267 | "name": "safeTransferFrom", 268 | "outputs": [], 269 | "stateMutability": "nonpayable", 270 | "type": "function" 271 | }, 272 | { 273 | "inputs": [ 274 | { 275 | "internalType": "address", 276 | "name": "from", 277 | "type": "address" 278 | }, 279 | { 280 | "internalType": "address", 281 | "name": "to", 282 | "type": "address" 283 | }, 284 | { 285 | "internalType": "uint256", 286 | "name": "tokenId", 287 | "type": "uint256" 288 | }, 289 | { 290 | "internalType": "bytes", 291 | "name": "data", 292 | "type": "bytes" 293 | } 294 | ], 295 | "name": "safeTransferFrom", 296 | "outputs": [], 297 | "stateMutability": "nonpayable", 298 | "type": "function" 299 | }, 300 | { 301 | "inputs": [ 302 | { 303 | "internalType": "address", 304 | "name": "operator", 305 | "type": "address" 306 | }, 307 | { 308 | "internalType": "bool", 309 | "name": "_approved", 310 | "type": "bool" 311 | } 312 | ], 313 | "name": "setApprovalForAll", 314 | "outputs": [], 315 | "stateMutability": "nonpayable", 316 | "type": "function" 317 | }, 318 | { 319 | "inputs": [ 320 | { 321 | "internalType": "bytes4", 322 | "name": "interfaceId", 323 | "type": "bytes4" 324 | } 325 | ], 326 | "name": "supportsInterface", 327 | "outputs": [ 328 | { 329 | "internalType": "bool", 330 | "name": "", 331 | "type": "bool" 332 | } 333 | ], 334 | "stateMutability": "view", 335 | "type": "function" 336 | }, 337 | { 338 | "inputs": [], 339 | "name": "symbol", 340 | "outputs": [ 341 | { 342 | "internalType": "string", 343 | "name": "", 344 | "type": "string" 345 | } 346 | ], 347 | "stateMutability": "view", 348 | "type": "function" 349 | }, 350 | { 351 | "inputs": [ 352 | { 353 | "internalType": "uint256", 354 | "name": "tokenId", 355 | "type": "uint256" 356 | } 357 | ], 358 | "name": "tokenURI", 359 | "outputs": [ 360 | { 361 | "internalType": "string", 362 | "name": "", 363 | "type": "string" 364 | } 365 | ], 366 | "stateMutability": "view", 367 | "type": "function" 368 | }, 369 | { 370 | "inputs": [], 371 | "name": "totalSupply", 372 | "outputs": [ 373 | { 374 | "internalType": "uint256", 375 | "name": "", 376 | "type": "uint256" 377 | } 378 | ], 379 | "stateMutability": "view", 380 | "type": "function" 381 | }, 382 | { 383 | "inputs": [ 384 | { 385 | "internalType": "address", 386 | "name": "from", 387 | "type": "address" 388 | }, 389 | { 390 | "internalType": "address", 391 | "name": "to", 392 | "type": "address" 393 | }, 394 | { 395 | "internalType": "uint256", 396 | "name": "tokenId", 397 | "type": "uint256" 398 | } 399 | ], 400 | "name": "transferFrom", 401 | "outputs": [], 402 | "stateMutability": "nonpayable", 403 | "type": "function" 404 | } 405 | ] -------------------------------------------------------------------------------- /packages/contracts/abi/IRenderer.sol/IRenderer.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "uint256", 6 | "name": "tokenId", 7 | "type": "uint256" 8 | } 9 | ], 10 | "name": "tokenURI", 11 | "outputs": [ 12 | { 13 | "internalType": "string", 14 | "name": "", 15 | "type": "string" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | } 21 | ] -------------------------------------------------------------------------------- /packages/contracts/abi/Ownable.sol/Ownable.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "previousOwner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "newOwner", 15 | "type": "address" 16 | } 17 | ], 18 | "name": "OwnershipTransferred", 19 | "type": "event" 20 | }, 21 | { 22 | "inputs": [], 23 | "name": "owner", 24 | "outputs": [ 25 | { 26 | "internalType": "address", 27 | "name": "", 28 | "type": "address" 29 | } 30 | ], 31 | "stateMutability": "view", 32 | "type": "function" 33 | }, 34 | { 35 | "inputs": [], 36 | "name": "renounceOwnership", 37 | "outputs": [], 38 | "stateMutability": "nonpayable", 39 | "type": "function" 40 | }, 41 | { 42 | "inputs": [ 43 | { 44 | "internalType": "address", 45 | "name": "newOwner", 46 | "type": "address" 47 | } 48 | ], 49 | "name": "transferOwnership", 50 | "outputs": [], 51 | "stateMutability": "nonpayable", 52 | "type": "function" 53 | } 54 | ] -------------------------------------------------------------------------------- /packages/contracts/deploy.sh: -------------------------------------------------------------------------------- 1 | # Expects jq to be installed 2 | 3 | source .env 4 | source .env.local 5 | 6 | if [ -z "$CHAIN_NAME" ]; then 7 | echo "CHAIN_NAME is not set" 8 | exit 1 9 | fi 10 | 11 | CONTRACT_NAME="ExampleNFT" 12 | 13 | DEPLOY_OUTPUT="deploys/$CHAIN_NAME/$CONTRACT_NAME.json" 14 | mkdir -p $(dirname $DEPLOY_OUTPUT) 15 | 16 | if [ ! -f $DEPLOY_OUTPUT ] || [ ! -s $DEPLOY_OUTPUT ]; then 17 | forge create $CONTRACT_NAME --json --rpc-url=$RPC_URL --private-key=$DEPLOYER_PRIVATE_KEY | jq . > $DEPLOY_OUTPUT 18 | fi 19 | 20 | CONTRACT_ADDRESS=$(cat $DEPLOY_OUTPUT | jq -r ".deployedTo") 21 | if [ -z $CONTRACT_ADDRESS ]; then 22 | echo "No contract address found in $DEPLOY_OUTPUT" 23 | exit 1 24 | fi 25 | 26 | echo "Using $CHAIN_NAME contract address: $CONTRACT_ADDRESS" 27 | 28 | # cast send --rpc-url=$RPC_URL $CONTRACT_ADDRESS "setBaseTokenURI(string)" "ipfs://somehashgoeshere" --private-key=$DEPLOYER_PRIVATE_KEY 29 | -------------------------------------------------------------------------------- /packages/contracts/deploys/goerli/ExampleNFT.json: -------------------------------------------------------------------------------- 1 | { 2 | "deployer": "0xF3337fe5A01039e77bcEeb3f6DF36Ee717B784b8", 3 | "deployedTo": "0xc1DaeF9c17622AeF615Ab7336078E0092e3d4832", 4 | "transactionHash": "0x553a470ed2487a7c932cf7bab730d8c8ba3fecf2621699a1de1a78b10bd60874" 5 | } 6 | -------------------------------------------------------------------------------- /packages/contracts/out/Context.sol/Context.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /packages/contracts/out/ECDSA.sol/ECDSA.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /packages/contracts/out/ERC20.sol/ERC20.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "string", 6 | "name": "name_", 7 | "type": "string" 8 | }, 9 | { 10 | "internalType": "string", 11 | "name": "symbol_", 12 | "type": "string" 13 | } 14 | ], 15 | "stateMutability": "nonpayable", 16 | "type": "constructor" 17 | }, 18 | { 19 | "anonymous": false, 20 | "inputs": [ 21 | { 22 | "indexed": true, 23 | "internalType": "address", 24 | "name": "owner", 25 | "type": "address" 26 | }, 27 | { 28 | "indexed": true, 29 | "internalType": "address", 30 | "name": "spender", 31 | "type": "address" 32 | }, 33 | { 34 | "indexed": false, 35 | "internalType": "uint256", 36 | "name": "value", 37 | "type": "uint256" 38 | } 39 | ], 40 | "name": "Approval", 41 | "type": "event" 42 | }, 43 | { 44 | "anonymous": false, 45 | "inputs": [ 46 | { 47 | "indexed": true, 48 | "internalType": "address", 49 | "name": "from", 50 | "type": "address" 51 | }, 52 | { 53 | "indexed": true, 54 | "internalType": "address", 55 | "name": "to", 56 | "type": "address" 57 | }, 58 | { 59 | "indexed": false, 60 | "internalType": "uint256", 61 | "name": "value", 62 | "type": "uint256" 63 | } 64 | ], 65 | "name": "Transfer", 66 | "type": "event" 67 | }, 68 | { 69 | "inputs": [ 70 | { 71 | "internalType": "address", 72 | "name": "owner", 73 | "type": "address" 74 | }, 75 | { 76 | "internalType": "address", 77 | "name": "spender", 78 | "type": "address" 79 | } 80 | ], 81 | "name": "allowance", 82 | "outputs": [ 83 | { 84 | "internalType": "uint256", 85 | "name": "", 86 | "type": "uint256" 87 | } 88 | ], 89 | "stateMutability": "view", 90 | "type": "function" 91 | }, 92 | { 93 | "inputs": [ 94 | { 95 | "internalType": "address", 96 | "name": "spender", 97 | "type": "address" 98 | }, 99 | { 100 | "internalType": "uint256", 101 | "name": "amount", 102 | "type": "uint256" 103 | } 104 | ], 105 | "name": "approve", 106 | "outputs": [ 107 | { 108 | "internalType": "bool", 109 | "name": "", 110 | "type": "bool" 111 | } 112 | ], 113 | "stateMutability": "nonpayable", 114 | "type": "function" 115 | }, 116 | { 117 | "inputs": [ 118 | { 119 | "internalType": "address", 120 | "name": "account", 121 | "type": "address" 122 | } 123 | ], 124 | "name": "balanceOf", 125 | "outputs": [ 126 | { 127 | "internalType": "uint256", 128 | "name": "", 129 | "type": "uint256" 130 | } 131 | ], 132 | "stateMutability": "view", 133 | "type": "function" 134 | }, 135 | { 136 | "inputs": [], 137 | "name": "decimals", 138 | "outputs": [ 139 | { 140 | "internalType": "uint8", 141 | "name": "", 142 | "type": "uint8" 143 | } 144 | ], 145 | "stateMutability": "view", 146 | "type": "function" 147 | }, 148 | { 149 | "inputs": [ 150 | { 151 | "internalType": "address", 152 | "name": "spender", 153 | "type": "address" 154 | }, 155 | { 156 | "internalType": "uint256", 157 | "name": "subtractedValue", 158 | "type": "uint256" 159 | } 160 | ], 161 | "name": "decreaseAllowance", 162 | "outputs": [ 163 | { 164 | "internalType": "bool", 165 | "name": "", 166 | "type": "bool" 167 | } 168 | ], 169 | "stateMutability": "nonpayable", 170 | "type": "function" 171 | }, 172 | { 173 | "inputs": [ 174 | { 175 | "internalType": "address", 176 | "name": "spender", 177 | "type": "address" 178 | }, 179 | { 180 | "internalType": "uint256", 181 | "name": "addedValue", 182 | "type": "uint256" 183 | } 184 | ], 185 | "name": "increaseAllowance", 186 | "outputs": [ 187 | { 188 | "internalType": "bool", 189 | "name": "", 190 | "type": "bool" 191 | } 192 | ], 193 | "stateMutability": "nonpayable", 194 | "type": "function" 195 | }, 196 | { 197 | "inputs": [], 198 | "name": "name", 199 | "outputs": [ 200 | { 201 | "internalType": "string", 202 | "name": "", 203 | "type": "string" 204 | } 205 | ], 206 | "stateMutability": "view", 207 | "type": "function" 208 | }, 209 | { 210 | "inputs": [], 211 | "name": "symbol", 212 | "outputs": [ 213 | { 214 | "internalType": "string", 215 | "name": "", 216 | "type": "string" 217 | } 218 | ], 219 | "stateMutability": "view", 220 | "type": "function" 221 | }, 222 | { 223 | "inputs": [], 224 | "name": "totalSupply", 225 | "outputs": [ 226 | { 227 | "internalType": "uint256", 228 | "name": "", 229 | "type": "uint256" 230 | } 231 | ], 232 | "stateMutability": "view", 233 | "type": "function" 234 | }, 235 | { 236 | "inputs": [ 237 | { 238 | "internalType": "address", 239 | "name": "to", 240 | "type": "address" 241 | }, 242 | { 243 | "internalType": "uint256", 244 | "name": "amount", 245 | "type": "uint256" 246 | } 247 | ], 248 | "name": "transfer", 249 | "outputs": [ 250 | { 251 | "internalType": "bool", 252 | "name": "", 253 | "type": "bool" 254 | } 255 | ], 256 | "stateMutability": "nonpayable", 257 | "type": "function" 258 | }, 259 | { 260 | "inputs": [ 261 | { 262 | "internalType": "address", 263 | "name": "from", 264 | "type": "address" 265 | }, 266 | { 267 | "internalType": "address", 268 | "name": "to", 269 | "type": "address" 270 | }, 271 | { 272 | "internalType": "uint256", 273 | "name": "amount", 274 | "type": "uint256" 275 | } 276 | ], 277 | "name": "transferFrom", 278 | "outputs": [ 279 | { 280 | "internalType": "bool", 281 | "name": "", 282 | "type": "bool" 283 | } 284 | ], 285 | "stateMutability": "nonpayable", 286 | "type": "function" 287 | } 288 | ] -------------------------------------------------------------------------------- /packages/contracts/out/ERC721A.sol/ERC721A.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "string", 6 | "name": "name_", 7 | "type": "string" 8 | }, 9 | { 10 | "internalType": "string", 11 | "name": "symbol_", 12 | "type": "string" 13 | } 14 | ], 15 | "stateMutability": "nonpayable", 16 | "type": "constructor" 17 | }, 18 | { 19 | "inputs": [], 20 | "name": "ApprovalCallerNotOwnerNorApproved", 21 | "type": "error" 22 | }, 23 | { 24 | "inputs": [], 25 | "name": "ApprovalQueryForNonexistentToken", 26 | "type": "error" 27 | }, 28 | { 29 | "inputs": [], 30 | "name": "ApproveToCaller", 31 | "type": "error" 32 | }, 33 | { 34 | "inputs": [], 35 | "name": "BalanceQueryForZeroAddress", 36 | "type": "error" 37 | }, 38 | { 39 | "inputs": [], 40 | "name": "MintToZeroAddress", 41 | "type": "error" 42 | }, 43 | { 44 | "inputs": [], 45 | "name": "MintZeroQuantity", 46 | "type": "error" 47 | }, 48 | { 49 | "inputs": [], 50 | "name": "OwnerQueryForNonexistentToken", 51 | "type": "error" 52 | }, 53 | { 54 | "inputs": [], 55 | "name": "TransferCallerNotOwnerNorApproved", 56 | "type": "error" 57 | }, 58 | { 59 | "inputs": [], 60 | "name": "TransferFromIncorrectOwner", 61 | "type": "error" 62 | }, 63 | { 64 | "inputs": [], 65 | "name": "TransferToNonERC721ReceiverImplementer", 66 | "type": "error" 67 | }, 68 | { 69 | "inputs": [], 70 | "name": "TransferToZeroAddress", 71 | "type": "error" 72 | }, 73 | { 74 | "inputs": [], 75 | "name": "URIQueryForNonexistentToken", 76 | "type": "error" 77 | }, 78 | { 79 | "anonymous": false, 80 | "inputs": [ 81 | { 82 | "indexed": true, 83 | "internalType": "address", 84 | "name": "owner", 85 | "type": "address" 86 | }, 87 | { 88 | "indexed": true, 89 | "internalType": "address", 90 | "name": "approved", 91 | "type": "address" 92 | }, 93 | { 94 | "indexed": true, 95 | "internalType": "uint256", 96 | "name": "tokenId", 97 | "type": "uint256" 98 | } 99 | ], 100 | "name": "Approval", 101 | "type": "event" 102 | }, 103 | { 104 | "anonymous": false, 105 | "inputs": [ 106 | { 107 | "indexed": true, 108 | "internalType": "address", 109 | "name": "owner", 110 | "type": "address" 111 | }, 112 | { 113 | "indexed": true, 114 | "internalType": "address", 115 | "name": "operator", 116 | "type": "address" 117 | }, 118 | { 119 | "indexed": false, 120 | "internalType": "bool", 121 | "name": "approved", 122 | "type": "bool" 123 | } 124 | ], 125 | "name": "ApprovalForAll", 126 | "type": "event" 127 | }, 128 | { 129 | "anonymous": false, 130 | "inputs": [ 131 | { 132 | "indexed": true, 133 | "internalType": "address", 134 | "name": "from", 135 | "type": "address" 136 | }, 137 | { 138 | "indexed": true, 139 | "internalType": "address", 140 | "name": "to", 141 | "type": "address" 142 | }, 143 | { 144 | "indexed": true, 145 | "internalType": "uint256", 146 | "name": "tokenId", 147 | "type": "uint256" 148 | } 149 | ], 150 | "name": "Transfer", 151 | "type": "event" 152 | }, 153 | { 154 | "inputs": [ 155 | { 156 | "internalType": "address", 157 | "name": "to", 158 | "type": "address" 159 | }, 160 | { 161 | "internalType": "uint256", 162 | "name": "tokenId", 163 | "type": "uint256" 164 | } 165 | ], 166 | "name": "approve", 167 | "outputs": [], 168 | "stateMutability": "nonpayable", 169 | "type": "function" 170 | }, 171 | { 172 | "inputs": [ 173 | { 174 | "internalType": "address", 175 | "name": "owner", 176 | "type": "address" 177 | } 178 | ], 179 | "name": "balanceOf", 180 | "outputs": [ 181 | { 182 | "internalType": "uint256", 183 | "name": "", 184 | "type": "uint256" 185 | } 186 | ], 187 | "stateMutability": "view", 188 | "type": "function" 189 | }, 190 | { 191 | "inputs": [ 192 | { 193 | "internalType": "uint256", 194 | "name": "tokenId", 195 | "type": "uint256" 196 | } 197 | ], 198 | "name": "getApproved", 199 | "outputs": [ 200 | { 201 | "internalType": "address", 202 | "name": "", 203 | "type": "address" 204 | } 205 | ], 206 | "stateMutability": "view", 207 | "type": "function" 208 | }, 209 | { 210 | "inputs": [ 211 | { 212 | "internalType": "address", 213 | "name": "owner", 214 | "type": "address" 215 | }, 216 | { 217 | "internalType": "address", 218 | "name": "operator", 219 | "type": "address" 220 | } 221 | ], 222 | "name": "isApprovedForAll", 223 | "outputs": [ 224 | { 225 | "internalType": "bool", 226 | "name": "", 227 | "type": "bool" 228 | } 229 | ], 230 | "stateMutability": "view", 231 | "type": "function" 232 | }, 233 | { 234 | "inputs": [], 235 | "name": "name", 236 | "outputs": [ 237 | { 238 | "internalType": "string", 239 | "name": "", 240 | "type": "string" 241 | } 242 | ], 243 | "stateMutability": "view", 244 | "type": "function" 245 | }, 246 | { 247 | "inputs": [ 248 | { 249 | "internalType": "uint256", 250 | "name": "tokenId", 251 | "type": "uint256" 252 | } 253 | ], 254 | "name": "ownerOf", 255 | "outputs": [ 256 | { 257 | "internalType": "address", 258 | "name": "", 259 | "type": "address" 260 | } 261 | ], 262 | "stateMutability": "view", 263 | "type": "function" 264 | }, 265 | { 266 | "inputs": [ 267 | { 268 | "internalType": "address", 269 | "name": "from", 270 | "type": "address" 271 | }, 272 | { 273 | "internalType": "address", 274 | "name": "to", 275 | "type": "address" 276 | }, 277 | { 278 | "internalType": "uint256", 279 | "name": "tokenId", 280 | "type": "uint256" 281 | } 282 | ], 283 | "name": "safeTransferFrom", 284 | "outputs": [], 285 | "stateMutability": "nonpayable", 286 | "type": "function" 287 | }, 288 | { 289 | "inputs": [ 290 | { 291 | "internalType": "address", 292 | "name": "from", 293 | "type": "address" 294 | }, 295 | { 296 | "internalType": "address", 297 | "name": "to", 298 | "type": "address" 299 | }, 300 | { 301 | "internalType": "uint256", 302 | "name": "tokenId", 303 | "type": "uint256" 304 | }, 305 | { 306 | "internalType": "bytes", 307 | "name": "_data", 308 | "type": "bytes" 309 | } 310 | ], 311 | "name": "safeTransferFrom", 312 | "outputs": [], 313 | "stateMutability": "nonpayable", 314 | "type": "function" 315 | }, 316 | { 317 | "inputs": [ 318 | { 319 | "internalType": "address", 320 | "name": "operator", 321 | "type": "address" 322 | }, 323 | { 324 | "internalType": "bool", 325 | "name": "approved", 326 | "type": "bool" 327 | } 328 | ], 329 | "name": "setApprovalForAll", 330 | "outputs": [], 331 | "stateMutability": "nonpayable", 332 | "type": "function" 333 | }, 334 | { 335 | "inputs": [ 336 | { 337 | "internalType": "bytes4", 338 | "name": "interfaceId", 339 | "type": "bytes4" 340 | } 341 | ], 342 | "name": "supportsInterface", 343 | "outputs": [ 344 | { 345 | "internalType": "bool", 346 | "name": "", 347 | "type": "bool" 348 | } 349 | ], 350 | "stateMutability": "view", 351 | "type": "function" 352 | }, 353 | { 354 | "inputs": [], 355 | "name": "symbol", 356 | "outputs": [ 357 | { 358 | "internalType": "string", 359 | "name": "", 360 | "type": "string" 361 | } 362 | ], 363 | "stateMutability": "view", 364 | "type": "function" 365 | }, 366 | { 367 | "inputs": [ 368 | { 369 | "internalType": "uint256", 370 | "name": "tokenId", 371 | "type": "uint256" 372 | } 373 | ], 374 | "name": "tokenURI", 375 | "outputs": [ 376 | { 377 | "internalType": "string", 378 | "name": "", 379 | "type": "string" 380 | } 381 | ], 382 | "stateMutability": "view", 383 | "type": "function" 384 | }, 385 | { 386 | "inputs": [], 387 | "name": "totalSupply", 388 | "outputs": [ 389 | { 390 | "internalType": "uint256", 391 | "name": "", 392 | "type": "uint256" 393 | } 394 | ], 395 | "stateMutability": "view", 396 | "type": "function" 397 | }, 398 | { 399 | "inputs": [ 400 | { 401 | "internalType": "address", 402 | "name": "from", 403 | "type": "address" 404 | }, 405 | { 406 | "internalType": "address", 407 | "name": "to", 408 | "type": "address" 409 | }, 410 | { 411 | "internalType": "uint256", 412 | "name": "tokenId", 413 | "type": "uint256" 414 | } 415 | ], 416 | "name": "transferFrom", 417 | "outputs": [], 418 | "stateMutability": "nonpayable", 419 | "type": "function" 420 | } 421 | ] -------------------------------------------------------------------------------- /packages/contracts/out/ERC721A.sol/ERC721A__IERC721Receiver.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "operator", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "from", 12 | "type": "address" 13 | }, 14 | { 15 | "internalType": "uint256", 16 | "name": "tokenId", 17 | "type": "uint256" 18 | }, 19 | { 20 | "internalType": "bytes", 21 | "name": "data", 22 | "type": "bytes" 23 | } 24 | ], 25 | "name": "onERC721Received", 26 | "outputs": [ 27 | { 28 | "internalType": "bytes4", 29 | "name": "", 30 | "type": "bytes4" 31 | } 32 | ], 33 | "stateMutability": "nonpayable", 34 | "type": "function" 35 | } 36 | ] -------------------------------------------------------------------------------- /packages/contracts/out/IERC165.sol/IERC165.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "bytes4", 6 | "name": "interfaceId", 7 | "type": "bytes4" 8 | } 9 | ], 10 | "name": "supportsInterface", 11 | "outputs": [ 12 | { 13 | "internalType": "bool", 14 | "name": "", 15 | "type": "bool" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | } 21 | ] -------------------------------------------------------------------------------- /packages/contracts/out/IERC20.sol/IERC20.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "owner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "spender", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "value", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Approval", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "from", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "to", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "value", 46 | "type": "uint256" 47 | } 48 | ], 49 | "name": "Transfer", 50 | "type": "event" 51 | }, 52 | { 53 | "inputs": [ 54 | { 55 | "internalType": "address", 56 | "name": "owner", 57 | "type": "address" 58 | }, 59 | { 60 | "internalType": "address", 61 | "name": "spender", 62 | "type": "address" 63 | } 64 | ], 65 | "name": "allowance", 66 | "outputs": [ 67 | { 68 | "internalType": "uint256", 69 | "name": "", 70 | "type": "uint256" 71 | } 72 | ], 73 | "stateMutability": "view", 74 | "type": "function" 75 | }, 76 | { 77 | "inputs": [ 78 | { 79 | "internalType": "address", 80 | "name": "spender", 81 | "type": "address" 82 | }, 83 | { 84 | "internalType": "uint256", 85 | "name": "amount", 86 | "type": "uint256" 87 | } 88 | ], 89 | "name": "approve", 90 | "outputs": [ 91 | { 92 | "internalType": "bool", 93 | "name": "", 94 | "type": "bool" 95 | } 96 | ], 97 | "stateMutability": "nonpayable", 98 | "type": "function" 99 | }, 100 | { 101 | "inputs": [ 102 | { 103 | "internalType": "address", 104 | "name": "account", 105 | "type": "address" 106 | } 107 | ], 108 | "name": "balanceOf", 109 | "outputs": [ 110 | { 111 | "internalType": "uint256", 112 | "name": "", 113 | "type": "uint256" 114 | } 115 | ], 116 | "stateMutability": "view", 117 | "type": "function" 118 | }, 119 | { 120 | "inputs": [], 121 | "name": "totalSupply", 122 | "outputs": [ 123 | { 124 | "internalType": "uint256", 125 | "name": "", 126 | "type": "uint256" 127 | } 128 | ], 129 | "stateMutability": "view", 130 | "type": "function" 131 | }, 132 | { 133 | "inputs": [ 134 | { 135 | "internalType": "address", 136 | "name": "to", 137 | "type": "address" 138 | }, 139 | { 140 | "internalType": "uint256", 141 | "name": "amount", 142 | "type": "uint256" 143 | } 144 | ], 145 | "name": "transfer", 146 | "outputs": [ 147 | { 148 | "internalType": "bool", 149 | "name": "", 150 | "type": "bool" 151 | } 152 | ], 153 | "stateMutability": "nonpayable", 154 | "type": "function" 155 | }, 156 | { 157 | "inputs": [ 158 | { 159 | "internalType": "address", 160 | "name": "from", 161 | "type": "address" 162 | }, 163 | { 164 | "internalType": "address", 165 | "name": "to", 166 | "type": "address" 167 | }, 168 | { 169 | "internalType": "uint256", 170 | "name": "amount", 171 | "type": "uint256" 172 | } 173 | ], 174 | "name": "transferFrom", 175 | "outputs": [ 176 | { 177 | "internalType": "bool", 178 | "name": "", 179 | "type": "bool" 180 | } 181 | ], 182 | "stateMutability": "nonpayable", 183 | "type": "function" 184 | } 185 | ] -------------------------------------------------------------------------------- /packages/contracts/out/IERC20Metadata.sol/IERC20Metadata.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "owner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "spender", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": false, 19 | "internalType": "uint256", 20 | "name": "value", 21 | "type": "uint256" 22 | } 23 | ], 24 | "name": "Approval", 25 | "type": "event" 26 | }, 27 | { 28 | "anonymous": false, 29 | "inputs": [ 30 | { 31 | "indexed": true, 32 | "internalType": "address", 33 | "name": "from", 34 | "type": "address" 35 | }, 36 | { 37 | "indexed": true, 38 | "internalType": "address", 39 | "name": "to", 40 | "type": "address" 41 | }, 42 | { 43 | "indexed": false, 44 | "internalType": "uint256", 45 | "name": "value", 46 | "type": "uint256" 47 | } 48 | ], 49 | "name": "Transfer", 50 | "type": "event" 51 | }, 52 | { 53 | "inputs": [ 54 | { 55 | "internalType": "address", 56 | "name": "owner", 57 | "type": "address" 58 | }, 59 | { 60 | "internalType": "address", 61 | "name": "spender", 62 | "type": "address" 63 | } 64 | ], 65 | "name": "allowance", 66 | "outputs": [ 67 | { 68 | "internalType": "uint256", 69 | "name": "", 70 | "type": "uint256" 71 | } 72 | ], 73 | "stateMutability": "view", 74 | "type": "function" 75 | }, 76 | { 77 | "inputs": [ 78 | { 79 | "internalType": "address", 80 | "name": "spender", 81 | "type": "address" 82 | }, 83 | { 84 | "internalType": "uint256", 85 | "name": "amount", 86 | "type": "uint256" 87 | } 88 | ], 89 | "name": "approve", 90 | "outputs": [ 91 | { 92 | "internalType": "bool", 93 | "name": "", 94 | "type": "bool" 95 | } 96 | ], 97 | "stateMutability": "nonpayable", 98 | "type": "function" 99 | }, 100 | { 101 | "inputs": [ 102 | { 103 | "internalType": "address", 104 | "name": "account", 105 | "type": "address" 106 | } 107 | ], 108 | "name": "balanceOf", 109 | "outputs": [ 110 | { 111 | "internalType": "uint256", 112 | "name": "", 113 | "type": "uint256" 114 | } 115 | ], 116 | "stateMutability": "view", 117 | "type": "function" 118 | }, 119 | { 120 | "inputs": [], 121 | "name": "decimals", 122 | "outputs": [ 123 | { 124 | "internalType": "uint8", 125 | "name": "", 126 | "type": "uint8" 127 | } 128 | ], 129 | "stateMutability": "view", 130 | "type": "function" 131 | }, 132 | { 133 | "inputs": [], 134 | "name": "name", 135 | "outputs": [ 136 | { 137 | "internalType": "string", 138 | "name": "", 139 | "type": "string" 140 | } 141 | ], 142 | "stateMutability": "view", 143 | "type": "function" 144 | }, 145 | { 146 | "inputs": [], 147 | "name": "symbol", 148 | "outputs": [ 149 | { 150 | "internalType": "string", 151 | "name": "", 152 | "type": "string" 153 | } 154 | ], 155 | "stateMutability": "view", 156 | "type": "function" 157 | }, 158 | { 159 | "inputs": [], 160 | "name": "totalSupply", 161 | "outputs": [ 162 | { 163 | "internalType": "uint256", 164 | "name": "", 165 | "type": "uint256" 166 | } 167 | ], 168 | "stateMutability": "view", 169 | "type": "function" 170 | }, 171 | { 172 | "inputs": [ 173 | { 174 | "internalType": "address", 175 | "name": "to", 176 | "type": "address" 177 | }, 178 | { 179 | "internalType": "uint256", 180 | "name": "amount", 181 | "type": "uint256" 182 | } 183 | ], 184 | "name": "transfer", 185 | "outputs": [ 186 | { 187 | "internalType": "bool", 188 | "name": "", 189 | "type": "bool" 190 | } 191 | ], 192 | "stateMutability": "nonpayable", 193 | "type": "function" 194 | }, 195 | { 196 | "inputs": [ 197 | { 198 | "internalType": "address", 199 | "name": "from", 200 | "type": "address" 201 | }, 202 | { 203 | "internalType": "address", 204 | "name": "to", 205 | "type": "address" 206 | }, 207 | { 208 | "internalType": "uint256", 209 | "name": "amount", 210 | "type": "uint256" 211 | } 212 | ], 213 | "name": "transferFrom", 214 | "outputs": [ 215 | { 216 | "internalType": "bool", 217 | "name": "", 218 | "type": "bool" 219 | } 220 | ], 221 | "stateMutability": "nonpayable", 222 | "type": "function" 223 | } 224 | ] -------------------------------------------------------------------------------- /packages/contracts/out/IERC2981.sol/IERC2981.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "uint256", 6 | "name": "tokenId", 7 | "type": "uint256" 8 | }, 9 | { 10 | "internalType": "uint256", 11 | "name": "salePrice", 12 | "type": "uint256" 13 | } 14 | ], 15 | "name": "royaltyInfo", 16 | "outputs": [ 17 | { 18 | "internalType": "address", 19 | "name": "receiver", 20 | "type": "address" 21 | }, 22 | { 23 | "internalType": "uint256", 24 | "name": "royaltyAmount", 25 | "type": "uint256" 26 | } 27 | ], 28 | "stateMutability": "view", 29 | "type": "function" 30 | }, 31 | { 32 | "inputs": [ 33 | { 34 | "internalType": "bytes4", 35 | "name": "interfaceId", 36 | "type": "bytes4" 37 | } 38 | ], 39 | "name": "supportsInterface", 40 | "outputs": [ 41 | { 42 | "internalType": "bool", 43 | "name": "", 44 | "type": "bool" 45 | } 46 | ], 47 | "stateMutability": "view", 48 | "type": "function" 49 | } 50 | ] -------------------------------------------------------------------------------- /packages/contracts/out/IERC721A.sol/IERC721A.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "name": "ApprovalCallerNotOwnerNorApproved", 5 | "type": "error" 6 | }, 7 | { 8 | "inputs": [], 9 | "name": "ApprovalQueryForNonexistentToken", 10 | "type": "error" 11 | }, 12 | { 13 | "inputs": [], 14 | "name": "ApproveToCaller", 15 | "type": "error" 16 | }, 17 | { 18 | "inputs": [], 19 | "name": "BalanceQueryForZeroAddress", 20 | "type": "error" 21 | }, 22 | { 23 | "inputs": [], 24 | "name": "MintToZeroAddress", 25 | "type": "error" 26 | }, 27 | { 28 | "inputs": [], 29 | "name": "MintZeroQuantity", 30 | "type": "error" 31 | }, 32 | { 33 | "inputs": [], 34 | "name": "OwnerQueryForNonexistentToken", 35 | "type": "error" 36 | }, 37 | { 38 | "inputs": [], 39 | "name": "TransferCallerNotOwnerNorApproved", 40 | "type": "error" 41 | }, 42 | { 43 | "inputs": [], 44 | "name": "TransferFromIncorrectOwner", 45 | "type": "error" 46 | }, 47 | { 48 | "inputs": [], 49 | "name": "TransferToNonERC721ReceiverImplementer", 50 | "type": "error" 51 | }, 52 | { 53 | "inputs": [], 54 | "name": "TransferToZeroAddress", 55 | "type": "error" 56 | }, 57 | { 58 | "inputs": [], 59 | "name": "URIQueryForNonexistentToken", 60 | "type": "error" 61 | }, 62 | { 63 | "anonymous": false, 64 | "inputs": [ 65 | { 66 | "indexed": true, 67 | "internalType": "address", 68 | "name": "owner", 69 | "type": "address" 70 | }, 71 | { 72 | "indexed": true, 73 | "internalType": "address", 74 | "name": "approved", 75 | "type": "address" 76 | }, 77 | { 78 | "indexed": true, 79 | "internalType": "uint256", 80 | "name": "tokenId", 81 | "type": "uint256" 82 | } 83 | ], 84 | "name": "Approval", 85 | "type": "event" 86 | }, 87 | { 88 | "anonymous": false, 89 | "inputs": [ 90 | { 91 | "indexed": true, 92 | "internalType": "address", 93 | "name": "owner", 94 | "type": "address" 95 | }, 96 | { 97 | "indexed": true, 98 | "internalType": "address", 99 | "name": "operator", 100 | "type": "address" 101 | }, 102 | { 103 | "indexed": false, 104 | "internalType": "bool", 105 | "name": "approved", 106 | "type": "bool" 107 | } 108 | ], 109 | "name": "ApprovalForAll", 110 | "type": "event" 111 | }, 112 | { 113 | "anonymous": false, 114 | "inputs": [ 115 | { 116 | "indexed": true, 117 | "internalType": "address", 118 | "name": "from", 119 | "type": "address" 120 | }, 121 | { 122 | "indexed": true, 123 | "internalType": "address", 124 | "name": "to", 125 | "type": "address" 126 | }, 127 | { 128 | "indexed": true, 129 | "internalType": "uint256", 130 | "name": "tokenId", 131 | "type": "uint256" 132 | } 133 | ], 134 | "name": "Transfer", 135 | "type": "event" 136 | }, 137 | { 138 | "inputs": [ 139 | { 140 | "internalType": "address", 141 | "name": "to", 142 | "type": "address" 143 | }, 144 | { 145 | "internalType": "uint256", 146 | "name": "tokenId", 147 | "type": "uint256" 148 | } 149 | ], 150 | "name": "approve", 151 | "outputs": [], 152 | "stateMutability": "nonpayable", 153 | "type": "function" 154 | }, 155 | { 156 | "inputs": [ 157 | { 158 | "internalType": "address", 159 | "name": "owner", 160 | "type": "address" 161 | } 162 | ], 163 | "name": "balanceOf", 164 | "outputs": [ 165 | { 166 | "internalType": "uint256", 167 | "name": "balance", 168 | "type": "uint256" 169 | } 170 | ], 171 | "stateMutability": "view", 172 | "type": "function" 173 | }, 174 | { 175 | "inputs": [ 176 | { 177 | "internalType": "uint256", 178 | "name": "tokenId", 179 | "type": "uint256" 180 | } 181 | ], 182 | "name": "getApproved", 183 | "outputs": [ 184 | { 185 | "internalType": "address", 186 | "name": "operator", 187 | "type": "address" 188 | } 189 | ], 190 | "stateMutability": "view", 191 | "type": "function" 192 | }, 193 | { 194 | "inputs": [ 195 | { 196 | "internalType": "address", 197 | "name": "owner", 198 | "type": "address" 199 | }, 200 | { 201 | "internalType": "address", 202 | "name": "operator", 203 | "type": "address" 204 | } 205 | ], 206 | "name": "isApprovedForAll", 207 | "outputs": [ 208 | { 209 | "internalType": "bool", 210 | "name": "", 211 | "type": "bool" 212 | } 213 | ], 214 | "stateMutability": "view", 215 | "type": "function" 216 | }, 217 | { 218 | "inputs": [], 219 | "name": "name", 220 | "outputs": [ 221 | { 222 | "internalType": "string", 223 | "name": "", 224 | "type": "string" 225 | } 226 | ], 227 | "stateMutability": "view", 228 | "type": "function" 229 | }, 230 | { 231 | "inputs": [ 232 | { 233 | "internalType": "uint256", 234 | "name": "tokenId", 235 | "type": "uint256" 236 | } 237 | ], 238 | "name": "ownerOf", 239 | "outputs": [ 240 | { 241 | "internalType": "address", 242 | "name": "owner", 243 | "type": "address" 244 | } 245 | ], 246 | "stateMutability": "view", 247 | "type": "function" 248 | }, 249 | { 250 | "inputs": [ 251 | { 252 | "internalType": "address", 253 | "name": "from", 254 | "type": "address" 255 | }, 256 | { 257 | "internalType": "address", 258 | "name": "to", 259 | "type": "address" 260 | }, 261 | { 262 | "internalType": "uint256", 263 | "name": "tokenId", 264 | "type": "uint256" 265 | } 266 | ], 267 | "name": "safeTransferFrom", 268 | "outputs": [], 269 | "stateMutability": "nonpayable", 270 | "type": "function" 271 | }, 272 | { 273 | "inputs": [ 274 | { 275 | "internalType": "address", 276 | "name": "from", 277 | "type": "address" 278 | }, 279 | { 280 | "internalType": "address", 281 | "name": "to", 282 | "type": "address" 283 | }, 284 | { 285 | "internalType": "uint256", 286 | "name": "tokenId", 287 | "type": "uint256" 288 | }, 289 | { 290 | "internalType": "bytes", 291 | "name": "data", 292 | "type": "bytes" 293 | } 294 | ], 295 | "name": "safeTransferFrom", 296 | "outputs": [], 297 | "stateMutability": "nonpayable", 298 | "type": "function" 299 | }, 300 | { 301 | "inputs": [ 302 | { 303 | "internalType": "address", 304 | "name": "operator", 305 | "type": "address" 306 | }, 307 | { 308 | "internalType": "bool", 309 | "name": "_approved", 310 | "type": "bool" 311 | } 312 | ], 313 | "name": "setApprovalForAll", 314 | "outputs": [], 315 | "stateMutability": "nonpayable", 316 | "type": "function" 317 | }, 318 | { 319 | "inputs": [ 320 | { 321 | "internalType": "bytes4", 322 | "name": "interfaceId", 323 | "type": "bytes4" 324 | } 325 | ], 326 | "name": "supportsInterface", 327 | "outputs": [ 328 | { 329 | "internalType": "bool", 330 | "name": "", 331 | "type": "bool" 332 | } 333 | ], 334 | "stateMutability": "view", 335 | "type": "function" 336 | }, 337 | { 338 | "inputs": [], 339 | "name": "symbol", 340 | "outputs": [ 341 | { 342 | "internalType": "string", 343 | "name": "", 344 | "type": "string" 345 | } 346 | ], 347 | "stateMutability": "view", 348 | "type": "function" 349 | }, 350 | { 351 | "inputs": [ 352 | { 353 | "internalType": "uint256", 354 | "name": "tokenId", 355 | "type": "uint256" 356 | } 357 | ], 358 | "name": "tokenURI", 359 | "outputs": [ 360 | { 361 | "internalType": "string", 362 | "name": "", 363 | "type": "string" 364 | } 365 | ], 366 | "stateMutability": "view", 367 | "type": "function" 368 | }, 369 | { 370 | "inputs": [], 371 | "name": "totalSupply", 372 | "outputs": [ 373 | { 374 | "internalType": "uint256", 375 | "name": "", 376 | "type": "uint256" 377 | } 378 | ], 379 | "stateMutability": "view", 380 | "type": "function" 381 | }, 382 | { 383 | "inputs": [ 384 | { 385 | "internalType": "address", 386 | "name": "from", 387 | "type": "address" 388 | }, 389 | { 390 | "internalType": "address", 391 | "name": "to", 392 | "type": "address" 393 | }, 394 | { 395 | "internalType": "uint256", 396 | "name": "tokenId", 397 | "type": "uint256" 398 | } 399 | ], 400 | "name": "transferFrom", 401 | "outputs": [], 402 | "stateMutability": "nonpayable", 403 | "type": "function" 404 | } 405 | ] -------------------------------------------------------------------------------- /packages/contracts/out/IRenderer.sol/IRenderer.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "uint256", 6 | "name": "tokenId", 7 | "type": "uint256" 8 | } 9 | ], 10 | "name": "tokenURI", 11 | "outputs": [ 12 | { 13 | "internalType": "string", 14 | "name": "", 15 | "type": "string" 16 | } 17 | ], 18 | "stateMutability": "view", 19 | "type": "function" 20 | } 21 | ] -------------------------------------------------------------------------------- /packages/contracts/out/Ownable.sol/Ownable.abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "anonymous": false, 4 | "inputs": [ 5 | { 6 | "indexed": true, 7 | "internalType": "address", 8 | "name": "previousOwner", 9 | "type": "address" 10 | }, 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "newOwner", 15 | "type": "address" 16 | } 17 | ], 18 | "name": "OwnershipTransferred", 19 | "type": "event" 20 | }, 21 | { 22 | "inputs": [], 23 | "name": "owner", 24 | "outputs": [ 25 | { 26 | "internalType": "address", 27 | "name": "", 28 | "type": "address" 29 | } 30 | ], 31 | "stateMutability": "view", 32 | "type": "function" 33 | }, 34 | { 35 | "inputs": [], 36 | "name": "renounceOwnership", 37 | "outputs": [], 38 | "stateMutability": "nonpayable", 39 | "type": "function" 40 | }, 41 | { 42 | "inputs": [ 43 | { 44 | "internalType": "address", 45 | "name": "newOwner", 46 | "type": "address" 47 | } 48 | ], 49 | "name": "transferOwnership", 50 | "outputs": [], 51 | "stateMutability": "nonpayable", 52 | "type": "function" 53 | } 54 | ] -------------------------------------------------------------------------------- /packages/contracts/out/Strings.sol/Strings.abi.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /packages/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web3-scaffold/contracts", 3 | "private": true, 4 | "engines": { 5 | "node": "16", 6 | "pnpm": "7" 7 | }, 8 | "scripts": { 9 | "test": "forge test --no-match-contract Forked", 10 | "test:fork": "source .env && forge test --fork-url $RPC_URL --match-contract Forked", 11 | "build": "pnpm abi:clean && pnpm abi:compile && pnpm abi:types", 12 | "abi:clean": "rm -rf abi types", 13 | "abi:compile": "forge build --out packages/contracts/abi --force --skip test script", 14 | "abi:types": "typechain --target ethers-v5 \"abi/**/*.sol/!(*.abi).json\" --out-dir types && tsc", 15 | "fmt": "forge fmt src test script", 16 | "lint": "solhint --config ../../.solhint.json \"src/**/*.sol\"", 17 | "lint:fix": "pnpm lint --fix" 18 | }, 19 | "devDependencies": { 20 | "@ethersproject/abi": "^5.0.0", 21 | "@ethersproject/bytes": "^5.0.0", 22 | "@ethersproject/providers": "^5.0.0", 23 | "@typechain/ethers-v5": "^10.2.0", 24 | "ethers": "^5.5.2", 25 | "solhint": "^3.3.7", 26 | "typechain": "^8.1.1", 27 | "typescript": "^4.5.5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/contracts/script/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frolic/web3-scaffold/7a1767b499b97e6b49de7535e6cc39cb4d8daf87/packages/contracts/script/.gitkeep -------------------------------------------------------------------------------- /packages/contracts/src/ERC721Base.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import {ERC721A} from "erc721a/contracts/ERC721A.sol"; 5 | import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; 6 | import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import { 8 | IERC2981, IERC165 9 | } from "@openzeppelin/contracts/interfaces/IERC2981.sol"; 10 | import {IRenderer} from "./IRenderer.sol"; 11 | 12 | /// @author frolic.eth 13 | /// @title ERC721 base contract 14 | /// @notice ERC721-specific functionality to keep the actual NFT contract more 15 | /// readable and focused on the mint/project mechanics. 16 | abstract contract ERC721Base is ERC721A, Ownable, IERC2981 { 17 | uint256 public immutable price; 18 | uint256 public immutable maxSupply; 19 | uint256 public immutable royaltyBasisPoints = 500; 20 | 21 | IRenderer public renderer; 22 | string public baseTokenURI; 23 | 24 | event Initialized(); 25 | event RendererUpdated(IRenderer previousRenderer, IRenderer newRenderer); 26 | event BaseTokenURIUpdated( 27 | string previousBaseTokenURI, string newBaseTokenURI 28 | ); 29 | 30 | // ****************** // 31 | // *** INITIALIZE *** // 32 | // ****************** // 33 | 34 | constructor( 35 | string memory _name, 36 | string memory _symbol, 37 | uint256 _price, 38 | uint256 _maxSupply 39 | ) ERC721A(_name, _symbol) { 40 | price = _price; 41 | maxSupply = _maxSupply; 42 | emit Initialized(); 43 | } 44 | 45 | function _startTokenId() internal pure override returns (uint256) { 46 | return 1; 47 | } 48 | 49 | function totalMinted() public view returns (uint256) { 50 | return _totalMinted(); 51 | } 52 | 53 | // ****************** // 54 | // *** CONDITIONS *** // 55 | // ****************** // 56 | 57 | error MintLimitExceeded(uint256 limit); 58 | error MintSupplyExceeded(uint256 supply); 59 | error WrongPayment(); 60 | 61 | modifier withinMintLimit(uint256 limit, uint256 numToBeMinted) { 62 | if (_numberMinted(_msgSender()) + numToBeMinted > limit) { 63 | revert MintLimitExceeded(limit); 64 | } 65 | _; 66 | } 67 | 68 | modifier withinSupply( 69 | uint256 supply, 70 | uint256 numMinted, 71 | uint256 numToBeMinted 72 | ) { 73 | if (numMinted + numToBeMinted > supply) { 74 | revert MintSupplyExceeded(supply); 75 | } 76 | _; 77 | } 78 | 79 | modifier withinMaxSupply(uint256 numToBeMinted) { 80 | if (_totalMinted() + numToBeMinted > maxSupply) { 81 | revert MintSupplyExceeded(maxSupply); 82 | } 83 | _; 84 | } 85 | 86 | modifier hasExactPayment(uint256 numToBeMinted) { 87 | if (msg.value != price * numToBeMinted) { 88 | revert WrongPayment(); 89 | } 90 | _; 91 | } 92 | 93 | // ************ // 94 | // *** MINT *** // 95 | // ************ // 96 | 97 | function _mintMany(address to, uint256 numToBeMinted) internal { 98 | _mintMany(to, numToBeMinted, ""); 99 | } 100 | 101 | function _mintMany(address to, uint256 numToBeMinted, bytes memory data) 102 | internal 103 | withinMaxSupply(numToBeMinted) 104 | { 105 | uint256 batchSize = 10; 106 | uint256 length = numToBeMinted / batchSize; 107 | for (uint256 i = 0; i < length;) { 108 | _safeMint(to, batchSize, data); 109 | unchecked { 110 | ++i; 111 | } 112 | } 113 | if (numToBeMinted % batchSize > 0) { 114 | _safeMint(to, numToBeMinted % batchSize, data); 115 | } 116 | } 117 | 118 | // ****************** // 119 | // *** AFTER MINT *** // 120 | // ****************** // 121 | 122 | function _baseURI() internal view override returns (string memory) { 123 | return baseTokenURI; 124 | } 125 | 126 | function tokenURI(uint256 tokenId) 127 | public 128 | view 129 | override 130 | returns (string memory) 131 | { 132 | if (address(renderer) != address(0)) { 133 | return renderer.tokenURI(tokenId); 134 | } 135 | return super.tokenURI(tokenId); 136 | } 137 | 138 | // ***************** // 139 | // *** ROYALTIES *** // 140 | // ***************** // 141 | 142 | function supportsInterface(bytes4 _interfaceId) 143 | public 144 | view 145 | override(ERC721A, IERC165) 146 | returns (bool) 147 | { 148 | return _interfaceId == type(IERC2981).interfaceId 149 | || super.supportsInterface(_interfaceId); 150 | } 151 | 152 | function royaltyInfo(uint256, uint256 salePrice) 153 | external 154 | view 155 | returns (address, uint256) 156 | { 157 | return (address(this), (salePrice * royaltyBasisPoints) / 10000); 158 | } 159 | 160 | // ************* // 161 | // *** ADMIN *** // 162 | // ************* // 163 | 164 | function setRenderer(IRenderer _renderer) external onlyOwner { 165 | emit RendererUpdated(renderer, _renderer); 166 | renderer = _renderer; 167 | } 168 | 169 | function setBaseTokenURI(string calldata _baseTokenURI) 170 | external 171 | onlyOwner 172 | { 173 | emit BaseTokenURIUpdated(baseTokenURI, _baseTokenURI); 174 | baseTokenURI = _baseTokenURI; 175 | } 176 | 177 | function withdrawAll() external { 178 | require(address(this).balance > 0, "Zero balance"); 179 | (bool sent,) = owner().call{value: address(this).balance}(""); 180 | require(sent, "Failed to withdraw"); 181 | } 182 | 183 | function withdrawAllERC20(IERC20 token) external { 184 | token.transfer(owner(), token.balanceOf(address(this))); 185 | } 186 | 187 | // Can be run any time after mint to optimize gas for future transfers 188 | function normalizeOwnership(uint256 startTokenId, uint256 quantity) 189 | external 190 | { 191 | for (uint256 i = 0; i < quantity; i++) { 192 | _initializeOwnershipAt(startTokenId + i); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /packages/contracts/src/ExampleNFT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | import {ERC721Base} from "./ERC721Base.sol"; 5 | 6 | /// @author frolic.eth 7 | /// @title Example NFT 8 | contract ExampleNFT is ERC721Base { 9 | // ****************** // 10 | // *** INITIALIZE *** // 11 | // ****************** // 12 | 13 | constructor() ERC721Base("Example NFT", "EXAMPLE", 0.1 ether, 10_000) {} 14 | 15 | // ************ // 16 | // *** MINT *** // 17 | // ************ // 18 | 19 | function mint(uint256 numToBeMinted) 20 | external 21 | payable 22 | hasExactPayment(numToBeMinted) 23 | withinMintLimit(4, numToBeMinted) 24 | { 25 | _mintMany(_msgSender(), numToBeMinted); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/contracts/src/IRenderer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.9; 3 | 4 | /// @author frolic.eth 5 | /// @title Upgradeable renderer interface 6 | /// @notice This leaves room for us to change how we return token metadata and 7 | /// unlocks future capability like fully on-chain storage. 8 | interface IRenderer { 9 | function tokenURI(uint256 tokenId) external view returns (string memory); 10 | } 11 | -------------------------------------------------------------------------------- /packages/contracts/test/ERC721Base.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | pragma solidity >=0.8.10 <0.9.0; 3 | 4 | import "forge-std/Test.sol"; 5 | import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 7 | import "../src/ERC721Base.sol"; 8 | import "../src/IRenderer.sol"; 9 | 10 | contract CustomNFT is ERC721Base { 11 | constructor() ERC721Base("Custom NFT", "NFT", 0.1 ether, 100) {} 12 | 13 | function mint(uint256 numToBeMinted) 14 | external 15 | payable 16 | hasExactPayment(numToBeMinted) 17 | withinMintLimit(4, numToBeMinted) 18 | { 19 | _mintMany(_msgSender(), numToBeMinted); 20 | } 21 | 22 | function uncappedMint(uint256 numToBeMinted) 23 | external 24 | payable 25 | hasExactPayment(numToBeMinted) 26 | { 27 | _mintMany(_msgSender(), numToBeMinted); 28 | } 29 | 30 | function startTimestamp(uint256 tokenId) public view returns (uint256) { 31 | return _ownershipOf(tokenId).startTimestamp; 32 | } 33 | } 34 | 35 | contract CustomRenderer is IRenderer { 36 | function tokenURI(uint256 tokenId) public pure returns (string memory) { 37 | return string.concat("{\"tokenId\":", Strings.toString(tokenId), "}"); 38 | } 39 | } 40 | 41 | contract CustomCoin is ERC20 { 42 | constructor() ERC20("Dummy ERC20", "DUMMY") {} 43 | 44 | function mint() public { 45 | _mint(_msgSender(), 100); 46 | } 47 | } 48 | 49 | contract ERC721BaseTest is Test { 50 | CustomNFT private nft; 51 | 52 | address private owner = 53 | vm.addr(uint256(keccak256(abi.encodePacked("owner")))); 54 | address private minter = 55 | vm.addr(uint256(keccak256(abi.encodePacked("minter")))); 56 | 57 | function setUp() public { 58 | nft = new CustomNFT(); 59 | nft.transferOwnership(owner); 60 | vm.deal(owner, 100 ether); 61 | vm.deal(minter, 100 ether); 62 | } 63 | 64 | function testMintPayment() public { 65 | vm.startPrank(minter); 66 | vm.expectRevert( 67 | abi.encodeWithSelector(ERC721Base.WrongPayment.selector) 68 | ); 69 | nft.mint(2); 70 | nft.mint{value: 0.2 ether}(2); 71 | } 72 | 73 | function testMintLimit() public { 74 | vm.startPrank(minter); 75 | nft.mint{value: 0.2 ether}(2); 76 | nft.mint{value: 0.1 ether}(1); 77 | vm.expectRevert( 78 | abi.encodeWithSelector(ERC721Base.MintLimitExceeded.selector, 4) 79 | ); 80 | nft.mint{value: 0.2 ether}(2); 81 | nft.mint{value: 0.1 ether}(1); 82 | vm.expectRevert( 83 | abi.encodeWithSelector(ERC721Base.MintLimitExceeded.selector, 4) 84 | ); 85 | nft.mint{value: 0.1 ether}(1); 86 | } 87 | 88 | function testMaxSupply() public { 89 | vm.startPrank(minter); 90 | nft.uncappedMint{value: 6 ether}(60); 91 | vm.expectRevert( 92 | abi.encodeWithSelector(ERC721Base.MintSupplyExceeded.selector, 100) 93 | ); 94 | nft.uncappedMint{value: 6 ether}(60); 95 | nft.uncappedMint{value: 4 ether}(40); 96 | vm.expectRevert( 97 | abi.encodeWithSelector(ERC721Base.MintSupplyExceeded.selector, 100) 98 | ); 99 | nft.uncappedMint{value: 0.1 ether}(1); 100 | } 101 | 102 | function testBaseTokenURI() public { 103 | vm.prank(owner); 104 | nft.setBaseTokenURI("ipfs://example/"); 105 | vm.prank(minter); 106 | nft.mint{value: 0.1 ether}(1); 107 | assertEq(nft.tokenURI(1), "ipfs://example/1"); 108 | } 109 | 110 | function testRenderer() public { 111 | CustomRenderer renderer = new CustomRenderer(); 112 | vm.prank(owner); 113 | nft.setRenderer(renderer); 114 | vm.prank(minter); 115 | nft.mint{value: 0.1 ether}(1); 116 | assertEq(nft.tokenURI(1), "{\"tokenId\":1}"); 117 | } 118 | 119 | function testWithdrawAll() public { 120 | assertEq(address(nft).balance, 0); 121 | assertEq(owner.balance, 100 ether); 122 | vm.expectRevert("Zero balance"); 123 | nft.withdrawAll(); 124 | vm.prank(minter); 125 | nft.mint{value: 0.2 ether}(2); 126 | assertEq(address(nft).balance, 0.2 ether); 127 | nft.withdrawAll(); 128 | assertEq(owner.balance, 100.2 ether); 129 | } 130 | 131 | function testWithdrawAllERC20() public { 132 | CustomCoin coin = new CustomCoin(); 133 | vm.prank(minter); 134 | coin.mint(); 135 | vm.prank(minter); 136 | coin.transfer(address(nft), 50); 137 | assertEq(coin.balanceOf(address(nft)), 50); 138 | assertEq(coin.balanceOf(address(owner)), 0); 139 | nft.withdrawAllERC20(coin); 140 | assertEq(coin.balanceOf(address(nft)), 0); 141 | assertEq(coin.balanceOf(address(owner)), 50); 142 | } 143 | 144 | function testOwnershipGas() public { 145 | vm.prank(minter); 146 | nft.uncappedMint{value: 10 ether}(100); 147 | 148 | uint256 startGas = gasleft(); 149 | nft.ownerOf(1); 150 | assertLt(startGas - gasleft(), 1400); 151 | startGas = gasleft(); 152 | nft.ownerOf(100); 153 | assertGt(startGas - gasleft(), 20_000); 154 | assertLt(startGas - gasleft(), 22_000); 155 | 156 | nft.normalizeOwnership(1, 100); 157 | startGas = gasleft(); 158 | nft.ownerOf(1); 159 | assertLt(startGas - gasleft(), 1400); 160 | startGas = gasleft(); 161 | nft.ownerOf(50); 162 | assertLt(startGas - gasleft(), 1400); 163 | } 164 | 165 | // Test to make sure ERC721A returns normalized startTimestamp 166 | function testStartTimestamp() public { 167 | vm.warp(3600); 168 | vm.prank(minter); 169 | nft.uncappedMint{value: 10 ether}(100); 170 | 171 | assertEq(nft.startTimestamp(1), 3600); 172 | assertEq(nft.startTimestamp(50), 3600); 173 | assertEq(nft.startTimestamp(100), 3600); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /packages/contracts/test/ExampleNFT.t.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: CC0-1.0 2 | pragma solidity >=0.8.10 <0.9.0; 3 | 4 | import "forge-std/Test.sol"; 5 | import "../src/ExampleNFT.sol"; 6 | import "../src/IRenderer.sol"; 7 | 8 | contract ExampleNFTTest is Test { 9 | ExampleNFT private nft; 10 | 11 | address private owner = mkaddr("owner"); 12 | address private minter = mkaddr("minter"); 13 | 14 | function mkaddr(string memory name) public returns (address) { 15 | address addr = 16 | address(uint160(uint256(keccak256(abi.encodePacked(name))))); 17 | vm.label(addr, name); 18 | return addr; 19 | } 20 | 21 | function setUp() public { 22 | nft = new ExampleNFT(); 23 | nft.transferOwnership(owner); 24 | vm.deal(owner, 10 ether); 25 | vm.deal(minter, 10 ether); 26 | } 27 | 28 | function testMint() public { 29 | assertEq(nft.balanceOf(minter), 0); 30 | 31 | vm.expectRevert(ERC721Base.WrongPayment.selector); 32 | nft.mint{value: 1 ether}(1); 33 | 34 | vm.prank(minter); 35 | nft.mint{value: 0.1 ether}(1); 36 | assertEq(nft.balanceOf(minter), 1); 37 | 38 | vm.prank(minter); 39 | nft.mint{value: 0.3 ether}(3); 40 | assertEq(nft.balanceOf(minter), 4); 41 | 42 | vm.prank(minter); 43 | vm.expectRevert( 44 | abi.encodeWithSelector(ERC721Base.MintLimitExceeded.selector, 4) 45 | ); 46 | nft.mint{value: 0.1 ether}(1); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["./types"], 3 | "compilerOptions": { 4 | "lib": ["esnext"], 5 | "target": "es6", 6 | "module": "commonjs", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "resolveJsonModule": true, 10 | "isolatedModules": true 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /packages/contracts/types/ERC721A.sol/ERC721A.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/ERC721A.sol/ERC721A__IERC721Receiver.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/ERC721A.sol/ERC721A__IERC721Receiver.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumber, 7 | BigNumberish, 8 | BytesLike, 9 | CallOverrides, 10 | ContractTransaction, 11 | Overrides, 12 | PopulatedTransaction, 13 | Signer, 14 | utils, 15 | } from "ethers"; 16 | import type { FunctionFragment, Result } from "@ethersproject/abi"; 17 | import type { Listener, Provider } from "@ethersproject/providers"; 18 | import type { 19 | TypedEventFilter, 20 | TypedEvent, 21 | TypedListener, 22 | OnEvent, 23 | PromiseOrValue, 24 | } from "../common"; 25 | 26 | export interface ERC721A__IERC721ReceiverInterface extends utils.Interface { 27 | functions: { 28 | "onERC721Received(address,address,uint256,bytes)": FunctionFragment; 29 | }; 30 | 31 | getFunction(nameOrSignatureOrTopic: "onERC721Received"): FunctionFragment; 32 | 33 | encodeFunctionData( 34 | functionFragment: "onERC721Received", 35 | values: [ 36 | PromiseOrValue, 37 | PromiseOrValue, 38 | PromiseOrValue, 39 | PromiseOrValue 40 | ] 41 | ): string; 42 | 43 | decodeFunctionResult( 44 | functionFragment: "onERC721Received", 45 | data: BytesLike 46 | ): Result; 47 | 48 | events: {}; 49 | } 50 | 51 | export interface ERC721A__IERC721Receiver extends BaseContract { 52 | connect(signerOrProvider: Signer | Provider | string): this; 53 | attach(addressOrName: string): this; 54 | deployed(): Promise; 55 | 56 | interface: ERC721A__IERC721ReceiverInterface; 57 | 58 | queryFilter( 59 | event: TypedEventFilter, 60 | fromBlockOrBlockhash?: string | number | undefined, 61 | toBlock?: string | number | undefined 62 | ): Promise>; 63 | 64 | listeners( 65 | eventFilter?: TypedEventFilter 66 | ): Array>; 67 | listeners(eventName?: string): Array; 68 | removeAllListeners( 69 | eventFilter: TypedEventFilter 70 | ): this; 71 | removeAllListeners(eventName?: string): this; 72 | off: OnEvent; 73 | on: OnEvent; 74 | once: OnEvent; 75 | removeListener: OnEvent; 76 | 77 | functions: { 78 | onERC721Received( 79 | operator: PromiseOrValue, 80 | from: PromiseOrValue, 81 | tokenId: PromiseOrValue, 82 | data: PromiseOrValue, 83 | overrides?: Overrides & { from?: PromiseOrValue } 84 | ): Promise; 85 | }; 86 | 87 | onERC721Received( 88 | operator: PromiseOrValue, 89 | from: PromiseOrValue, 90 | tokenId: PromiseOrValue, 91 | data: PromiseOrValue, 92 | overrides?: Overrides & { from?: PromiseOrValue } 93 | ): Promise; 94 | 95 | callStatic: { 96 | onERC721Received( 97 | operator: PromiseOrValue, 98 | from: PromiseOrValue, 99 | tokenId: PromiseOrValue, 100 | data: PromiseOrValue, 101 | overrides?: CallOverrides 102 | ): Promise; 103 | }; 104 | 105 | filters: {}; 106 | 107 | estimateGas: { 108 | onERC721Received( 109 | operator: PromiseOrValue, 110 | from: PromiseOrValue, 111 | tokenId: PromiseOrValue, 112 | data: PromiseOrValue, 113 | overrides?: Overrides & { from?: PromiseOrValue } 114 | ): Promise; 115 | }; 116 | 117 | populateTransaction: { 118 | onERC721Received( 119 | operator: PromiseOrValue, 120 | from: PromiseOrValue, 121 | tokenId: PromiseOrValue, 122 | data: PromiseOrValue, 123 | overrides?: Overrides & { from?: PromiseOrValue } 124 | ): Promise; 125 | }; 126 | } 127 | -------------------------------------------------------------------------------- /packages/contracts/types/ERC721A.sol/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/ERC721A.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export type { ERC721A } from "./ERC721A"; 5 | export type { ERC721A__IERC721Receiver } from "./ERC721A__IERC721Receiver"; 6 | -------------------------------------------------------------------------------- /packages/contracts/types/ERC721Base.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/ExampleNFT.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/IERC165.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/IERC165.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumber, 7 | BytesLike, 8 | CallOverrides, 9 | PopulatedTransaction, 10 | Signer, 11 | utils, 12 | } from "ethers"; 13 | import type { FunctionFragment, Result } from "@ethersproject/abi"; 14 | import type { Listener, Provider } from "@ethersproject/providers"; 15 | import type { 16 | TypedEventFilter, 17 | TypedEvent, 18 | TypedListener, 19 | OnEvent, 20 | PromiseOrValue, 21 | } from "./common"; 22 | 23 | export interface IERC165Interface extends utils.Interface { 24 | functions: { 25 | "supportsInterface(bytes4)": FunctionFragment; 26 | }; 27 | 28 | getFunction(nameOrSignatureOrTopic: "supportsInterface"): FunctionFragment; 29 | 30 | encodeFunctionData( 31 | functionFragment: "supportsInterface", 32 | values: [PromiseOrValue] 33 | ): string; 34 | 35 | decodeFunctionResult( 36 | functionFragment: "supportsInterface", 37 | data: BytesLike 38 | ): Result; 39 | 40 | events: {}; 41 | } 42 | 43 | export interface IERC165 extends BaseContract { 44 | connect(signerOrProvider: Signer | Provider | string): this; 45 | attach(addressOrName: string): this; 46 | deployed(): Promise; 47 | 48 | interface: IERC165Interface; 49 | 50 | queryFilter( 51 | event: TypedEventFilter, 52 | fromBlockOrBlockhash?: string | number | undefined, 53 | toBlock?: string | number | undefined 54 | ): Promise>; 55 | 56 | listeners( 57 | eventFilter?: TypedEventFilter 58 | ): Array>; 59 | listeners(eventName?: string): Array; 60 | removeAllListeners( 61 | eventFilter: TypedEventFilter 62 | ): this; 63 | removeAllListeners(eventName?: string): this; 64 | off: OnEvent; 65 | on: OnEvent; 66 | once: OnEvent; 67 | removeListener: OnEvent; 68 | 69 | functions: { 70 | supportsInterface( 71 | interfaceId: PromiseOrValue, 72 | overrides?: CallOverrides 73 | ): Promise<[boolean]>; 74 | }; 75 | 76 | supportsInterface( 77 | interfaceId: PromiseOrValue, 78 | overrides?: CallOverrides 79 | ): Promise; 80 | 81 | callStatic: { 82 | supportsInterface( 83 | interfaceId: PromiseOrValue, 84 | overrides?: CallOverrides 85 | ): Promise; 86 | }; 87 | 88 | filters: {}; 89 | 90 | estimateGas: { 91 | supportsInterface( 92 | interfaceId: PromiseOrValue, 93 | overrides?: CallOverrides 94 | ): Promise; 95 | }; 96 | 97 | populateTransaction: { 98 | supportsInterface( 99 | interfaceId: PromiseOrValue, 100 | overrides?: CallOverrides 101 | ): Promise; 102 | }; 103 | } 104 | -------------------------------------------------------------------------------- /packages/contracts/types/IERC20.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/IERC2981.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/IERC2981.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumber, 7 | BigNumberish, 8 | BytesLike, 9 | CallOverrides, 10 | PopulatedTransaction, 11 | Signer, 12 | utils, 13 | } from "ethers"; 14 | import type { FunctionFragment, Result } from "@ethersproject/abi"; 15 | import type { Listener, Provider } from "@ethersproject/providers"; 16 | import type { 17 | TypedEventFilter, 18 | TypedEvent, 19 | TypedListener, 20 | OnEvent, 21 | PromiseOrValue, 22 | } from "./common"; 23 | 24 | export interface IERC2981Interface extends utils.Interface { 25 | functions: { 26 | "royaltyInfo(uint256,uint256)": FunctionFragment; 27 | "supportsInterface(bytes4)": FunctionFragment; 28 | }; 29 | 30 | getFunction( 31 | nameOrSignatureOrTopic: "royaltyInfo" | "supportsInterface" 32 | ): FunctionFragment; 33 | 34 | encodeFunctionData( 35 | functionFragment: "royaltyInfo", 36 | values: [PromiseOrValue, PromiseOrValue] 37 | ): string; 38 | encodeFunctionData( 39 | functionFragment: "supportsInterface", 40 | values: [PromiseOrValue] 41 | ): string; 42 | 43 | decodeFunctionResult( 44 | functionFragment: "royaltyInfo", 45 | data: BytesLike 46 | ): Result; 47 | decodeFunctionResult( 48 | functionFragment: "supportsInterface", 49 | data: BytesLike 50 | ): Result; 51 | 52 | events: {}; 53 | } 54 | 55 | export interface IERC2981 extends BaseContract { 56 | connect(signerOrProvider: Signer | Provider | string): this; 57 | attach(addressOrName: string): this; 58 | deployed(): Promise; 59 | 60 | interface: IERC2981Interface; 61 | 62 | queryFilter( 63 | event: TypedEventFilter, 64 | fromBlockOrBlockhash?: string | number | undefined, 65 | toBlock?: string | number | undefined 66 | ): Promise>; 67 | 68 | listeners( 69 | eventFilter?: TypedEventFilter 70 | ): Array>; 71 | listeners(eventName?: string): Array; 72 | removeAllListeners( 73 | eventFilter: TypedEventFilter 74 | ): this; 75 | removeAllListeners(eventName?: string): this; 76 | off: OnEvent; 77 | on: OnEvent; 78 | once: OnEvent; 79 | removeListener: OnEvent; 80 | 81 | functions: { 82 | royaltyInfo( 83 | tokenId: PromiseOrValue, 84 | salePrice: PromiseOrValue, 85 | overrides?: CallOverrides 86 | ): Promise< 87 | [string, BigNumber] & { receiver: string; royaltyAmount: BigNumber } 88 | >; 89 | 90 | supportsInterface( 91 | interfaceId: PromiseOrValue, 92 | overrides?: CallOverrides 93 | ): Promise<[boolean]>; 94 | }; 95 | 96 | royaltyInfo( 97 | tokenId: PromiseOrValue, 98 | salePrice: PromiseOrValue, 99 | overrides?: CallOverrides 100 | ): Promise< 101 | [string, BigNumber] & { receiver: string; royaltyAmount: BigNumber } 102 | >; 103 | 104 | supportsInterface( 105 | interfaceId: PromiseOrValue, 106 | overrides?: CallOverrides 107 | ): Promise; 108 | 109 | callStatic: { 110 | royaltyInfo( 111 | tokenId: PromiseOrValue, 112 | salePrice: PromiseOrValue, 113 | overrides?: CallOverrides 114 | ): Promise< 115 | [string, BigNumber] & { receiver: string; royaltyAmount: BigNumber } 116 | >; 117 | 118 | supportsInterface( 119 | interfaceId: PromiseOrValue, 120 | overrides?: CallOverrides 121 | ): Promise; 122 | }; 123 | 124 | filters: {}; 125 | 126 | estimateGas: { 127 | royaltyInfo( 128 | tokenId: PromiseOrValue, 129 | salePrice: PromiseOrValue, 130 | overrides?: CallOverrides 131 | ): Promise; 132 | 133 | supportsInterface( 134 | interfaceId: PromiseOrValue, 135 | overrides?: CallOverrides 136 | ): Promise; 137 | }; 138 | 139 | populateTransaction: { 140 | royaltyInfo( 141 | tokenId: PromiseOrValue, 142 | salePrice: PromiseOrValue, 143 | overrides?: CallOverrides 144 | ): Promise; 145 | 146 | supportsInterface( 147 | interfaceId: PromiseOrValue, 148 | overrides?: CallOverrides 149 | ): Promise; 150 | }; 151 | } 152 | -------------------------------------------------------------------------------- /packages/contracts/types/IERC721A.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/IRenderer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/IRenderer.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumber, 7 | BigNumberish, 8 | BytesLike, 9 | CallOverrides, 10 | PopulatedTransaction, 11 | Signer, 12 | utils, 13 | } from "ethers"; 14 | import type { FunctionFragment, Result } from "@ethersproject/abi"; 15 | import type { Listener, Provider } from "@ethersproject/providers"; 16 | import type { 17 | TypedEventFilter, 18 | TypedEvent, 19 | TypedListener, 20 | OnEvent, 21 | PromiseOrValue, 22 | } from "./common"; 23 | 24 | export interface IRendererInterface extends utils.Interface { 25 | functions: { 26 | "tokenURI(uint256)": FunctionFragment; 27 | }; 28 | 29 | getFunction(nameOrSignatureOrTopic: "tokenURI"): FunctionFragment; 30 | 31 | encodeFunctionData( 32 | functionFragment: "tokenURI", 33 | values: [PromiseOrValue] 34 | ): string; 35 | 36 | decodeFunctionResult(functionFragment: "tokenURI", data: BytesLike): Result; 37 | 38 | events: {}; 39 | } 40 | 41 | export interface IRenderer extends BaseContract { 42 | connect(signerOrProvider: Signer | Provider | string): this; 43 | attach(addressOrName: string): this; 44 | deployed(): Promise; 45 | 46 | interface: IRendererInterface; 47 | 48 | queryFilter( 49 | event: TypedEventFilter, 50 | fromBlockOrBlockhash?: string | number | undefined, 51 | toBlock?: string | number | undefined 52 | ): Promise>; 53 | 54 | listeners( 55 | eventFilter?: TypedEventFilter 56 | ): Array>; 57 | listeners(eventName?: string): Array; 58 | removeAllListeners( 59 | eventFilter: TypedEventFilter 60 | ): this; 61 | removeAllListeners(eventName?: string): this; 62 | off: OnEvent; 63 | on: OnEvent; 64 | once: OnEvent; 65 | removeListener: OnEvent; 66 | 67 | functions: { 68 | tokenURI( 69 | tokenId: PromiseOrValue, 70 | overrides?: CallOverrides 71 | ): Promise<[string]>; 72 | }; 73 | 74 | tokenURI( 75 | tokenId: PromiseOrValue, 76 | overrides?: CallOverrides 77 | ): Promise; 78 | 79 | callStatic: { 80 | tokenURI( 81 | tokenId: PromiseOrValue, 82 | overrides?: CallOverrides 83 | ): Promise; 84 | }; 85 | 86 | filters: {}; 87 | 88 | estimateGas: { 89 | tokenURI( 90 | tokenId: PromiseOrValue, 91 | overrides?: CallOverrides 92 | ): Promise; 93 | }; 94 | 95 | populateTransaction: { 96 | tokenURI( 97 | tokenId: PromiseOrValue, 98 | overrides?: CallOverrides 99 | ): Promise; 100 | }; 101 | } 102 | -------------------------------------------------------------------------------- /packages/contracts/types/Ownable.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/Ownable.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { 5 | BaseContract, 6 | BigNumber, 7 | BytesLike, 8 | CallOverrides, 9 | ContractTransaction, 10 | Overrides, 11 | PopulatedTransaction, 12 | Signer, 13 | utils, 14 | } from "ethers"; 15 | import type { 16 | FunctionFragment, 17 | Result, 18 | EventFragment, 19 | } from "@ethersproject/abi"; 20 | import type { Listener, Provider } from "@ethersproject/providers"; 21 | import type { 22 | TypedEventFilter, 23 | TypedEvent, 24 | TypedListener, 25 | OnEvent, 26 | PromiseOrValue, 27 | } from "./common"; 28 | 29 | export interface OwnableInterface extends utils.Interface { 30 | functions: { 31 | "owner()": FunctionFragment; 32 | "renounceOwnership()": FunctionFragment; 33 | "transferOwnership(address)": FunctionFragment; 34 | }; 35 | 36 | getFunction( 37 | nameOrSignatureOrTopic: "owner" | "renounceOwnership" | "transferOwnership" 38 | ): FunctionFragment; 39 | 40 | encodeFunctionData(functionFragment: "owner", values?: undefined): string; 41 | encodeFunctionData( 42 | functionFragment: "renounceOwnership", 43 | values?: undefined 44 | ): string; 45 | encodeFunctionData( 46 | functionFragment: "transferOwnership", 47 | values: [PromiseOrValue] 48 | ): string; 49 | 50 | decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result; 51 | decodeFunctionResult( 52 | functionFragment: "renounceOwnership", 53 | data: BytesLike 54 | ): Result; 55 | decodeFunctionResult( 56 | functionFragment: "transferOwnership", 57 | data: BytesLike 58 | ): Result; 59 | 60 | events: { 61 | "OwnershipTransferred(address,address)": EventFragment; 62 | }; 63 | 64 | getEvent(nameOrSignatureOrTopic: "OwnershipTransferred"): EventFragment; 65 | } 66 | 67 | export interface OwnershipTransferredEventObject { 68 | previousOwner: string; 69 | newOwner: string; 70 | } 71 | export type OwnershipTransferredEvent = TypedEvent< 72 | [string, string], 73 | OwnershipTransferredEventObject 74 | >; 75 | 76 | export type OwnershipTransferredEventFilter = 77 | TypedEventFilter; 78 | 79 | export interface Ownable extends BaseContract { 80 | connect(signerOrProvider: Signer | Provider | string): this; 81 | attach(addressOrName: string): this; 82 | deployed(): Promise; 83 | 84 | interface: OwnableInterface; 85 | 86 | queryFilter( 87 | event: TypedEventFilter, 88 | fromBlockOrBlockhash?: string | number | undefined, 89 | toBlock?: string | number | undefined 90 | ): Promise>; 91 | 92 | listeners( 93 | eventFilter?: TypedEventFilter 94 | ): Array>; 95 | listeners(eventName?: string): Array; 96 | removeAllListeners( 97 | eventFilter: TypedEventFilter 98 | ): this; 99 | removeAllListeners(eventName?: string): this; 100 | off: OnEvent; 101 | on: OnEvent; 102 | once: OnEvent; 103 | removeListener: OnEvent; 104 | 105 | functions: { 106 | owner(overrides?: CallOverrides): Promise<[string]>; 107 | 108 | renounceOwnership( 109 | overrides?: Overrides & { from?: PromiseOrValue } 110 | ): Promise; 111 | 112 | transferOwnership( 113 | newOwner: PromiseOrValue, 114 | overrides?: Overrides & { from?: PromiseOrValue } 115 | ): Promise; 116 | }; 117 | 118 | owner(overrides?: CallOverrides): Promise; 119 | 120 | renounceOwnership( 121 | overrides?: Overrides & { from?: PromiseOrValue } 122 | ): Promise; 123 | 124 | transferOwnership( 125 | newOwner: PromiseOrValue, 126 | overrides?: Overrides & { from?: PromiseOrValue } 127 | ): Promise; 128 | 129 | callStatic: { 130 | owner(overrides?: CallOverrides): Promise; 131 | 132 | renounceOwnership(overrides?: CallOverrides): Promise; 133 | 134 | transferOwnership( 135 | newOwner: PromiseOrValue, 136 | overrides?: CallOverrides 137 | ): Promise; 138 | }; 139 | 140 | filters: { 141 | "OwnershipTransferred(address,address)"( 142 | previousOwner?: PromiseOrValue | null, 143 | newOwner?: PromiseOrValue | null 144 | ): OwnershipTransferredEventFilter; 145 | OwnershipTransferred( 146 | previousOwner?: PromiseOrValue | null, 147 | newOwner?: PromiseOrValue | null 148 | ): OwnershipTransferredEventFilter; 149 | }; 150 | 151 | estimateGas: { 152 | owner(overrides?: CallOverrides): Promise; 153 | 154 | renounceOwnership( 155 | overrides?: Overrides & { from?: PromiseOrValue } 156 | ): Promise; 157 | 158 | transferOwnership( 159 | newOwner: PromiseOrValue, 160 | overrides?: Overrides & { from?: PromiseOrValue } 161 | ): Promise; 162 | }; 163 | 164 | populateTransaction: { 165 | owner(overrides?: CallOverrides): Promise; 166 | 167 | renounceOwnership( 168 | overrides?: Overrides & { from?: PromiseOrValue } 169 | ): Promise; 170 | 171 | transferOwnership( 172 | newOwner: PromiseOrValue, 173 | overrides?: Overrides & { from?: PromiseOrValue } 174 | ): Promise; 175 | }; 176 | } 177 | -------------------------------------------------------------------------------- /packages/contracts/types/common.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /packages/contracts/types/common.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type { Listener } from "@ethersproject/providers"; 5 | import type { Event, EventFilter } from "ethers"; 6 | 7 | export interface TypedEvent< 8 | TArgsArray extends Array = any, 9 | TArgsObject = any 10 | > extends Event { 11 | args: TArgsArray & TArgsObject; 12 | } 13 | 14 | export interface TypedEventFilter<_TEvent extends TypedEvent> 15 | extends EventFilter {} 16 | 17 | export interface TypedListener { 18 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void; 19 | } 20 | 21 | type __TypechainArgsArray = T extends TypedEvent ? U : never; 22 | 23 | export interface OnEvent { 24 | ( 25 | eventFilter: TypedEventFilter, 26 | listener: TypedListener 27 | ): TRes; 28 | (eventName: string, listener: Listener): TRes; 29 | } 30 | 31 | export type MinEthersFactory = { 32 | deploy(...a: ARGS[]): Promise; 33 | }; 34 | 35 | export type GetContractTypeFromFactory = F extends MinEthersFactory< 36 | infer C, 37 | any 38 | > 39 | ? C 40 | : never; 41 | 42 | export type GetARGsTypeFromFactory = F extends MinEthersFactory 43 | ? Parameters 44 | : never; 45 | 46 | export type PromiseOrValue = T | Promise; 47 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/ERC721A.sol/ERC721A__IERC721Receiver__factory.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* Autogenerated file. Do not edit manually. */ 3 | /* tslint:disable */ 4 | /* eslint-disable */ 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.ERC721A__IERC721Receiver__factory = void 0; 7 | const ethers_1 = require("ethers"); 8 | const _abi = [ 9 | { 10 | inputs: [ 11 | { 12 | internalType: "address", 13 | name: "operator", 14 | type: "address", 15 | }, 16 | { 17 | internalType: "address", 18 | name: "from", 19 | type: "address", 20 | }, 21 | { 22 | internalType: "uint256", 23 | name: "tokenId", 24 | type: "uint256", 25 | }, 26 | { 27 | internalType: "bytes", 28 | name: "data", 29 | type: "bytes", 30 | }, 31 | ], 32 | name: "onERC721Received", 33 | outputs: [ 34 | { 35 | internalType: "bytes4", 36 | name: "", 37 | type: "bytes4", 38 | }, 39 | ], 40 | stateMutability: "nonpayable", 41 | type: "function", 42 | }, 43 | ]; 44 | class ERC721A__IERC721Receiver__factory { 45 | static createInterface() { 46 | return new ethers_1.utils.Interface(_abi); 47 | } 48 | static connect(address, signerOrProvider) { 49 | return new ethers_1.Contract(address, _abi, signerOrProvider); 50 | } 51 | } 52 | exports.ERC721A__IERC721Receiver__factory = ERC721A__IERC721Receiver__factory; 53 | ERC721A__IERC721Receiver__factory.abi = _abi; 54 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/ERC721A.sol/ERC721A__IERC721Receiver__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { 8 | ERC721A__IERC721Receiver, 9 | ERC721A__IERC721ReceiverInterface, 10 | } from "../../ERC721A.sol/ERC721A__IERC721Receiver"; 11 | 12 | const _abi = [ 13 | { 14 | inputs: [ 15 | { 16 | internalType: "address", 17 | name: "operator", 18 | type: "address", 19 | }, 20 | { 21 | internalType: "address", 22 | name: "from", 23 | type: "address", 24 | }, 25 | { 26 | internalType: "uint256", 27 | name: "tokenId", 28 | type: "uint256", 29 | }, 30 | { 31 | internalType: "bytes", 32 | name: "data", 33 | type: "bytes", 34 | }, 35 | ], 36 | name: "onERC721Received", 37 | outputs: [ 38 | { 39 | internalType: "bytes4", 40 | name: "", 41 | type: "bytes4", 42 | }, 43 | ], 44 | stateMutability: "nonpayable", 45 | type: "function", 46 | }, 47 | ] as const; 48 | 49 | export class ERC721A__IERC721Receiver__factory { 50 | static readonly abi = _abi; 51 | static createInterface(): ERC721A__IERC721ReceiverInterface { 52 | return new utils.Interface(_abi) as ERC721A__IERC721ReceiverInterface; 53 | } 54 | static connect( 55 | address: string, 56 | signerOrProvider: Signer | Provider 57 | ): ERC721A__IERC721Receiver { 58 | return new Contract( 59 | address, 60 | _abi, 61 | signerOrProvider 62 | ) as ERC721A__IERC721Receiver; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/ERC721A.sol/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.ERC721A__IERC721Receiver__factory = exports.ERC721A__factory = void 0; 4 | /* Autogenerated file. Do not edit manually. */ 5 | /* tslint:disable */ 6 | /* eslint-disable */ 7 | var ERC721A__factory_1 = require("./ERC721A__factory"); 8 | Object.defineProperty(exports, "ERC721A__factory", { enumerable: true, get: function () { return ERC721A__factory_1.ERC721A__factory; } }); 9 | var ERC721A__IERC721Receiver__factory_1 = require("./ERC721A__IERC721Receiver__factory"); 10 | Object.defineProperty(exports, "ERC721A__IERC721Receiver__factory", { enumerable: true, get: function () { return ERC721A__IERC721Receiver__factory_1.ERC721A__IERC721Receiver__factory; } }); 11 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/ERC721A.sol/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export { ERC721A__factory } from "./ERC721A__factory"; 5 | export { ERC721A__IERC721Receiver__factory } from "./ERC721A__IERC721Receiver__factory"; 6 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/IERC165__factory.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* Autogenerated file. Do not edit manually. */ 3 | /* tslint:disable */ 4 | /* eslint-disable */ 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.IERC165__factory = void 0; 7 | const ethers_1 = require("ethers"); 8 | const _abi = [ 9 | { 10 | inputs: [ 11 | { 12 | internalType: "bytes4", 13 | name: "interfaceId", 14 | type: "bytes4", 15 | }, 16 | ], 17 | name: "supportsInterface", 18 | outputs: [ 19 | { 20 | internalType: "bool", 21 | name: "", 22 | type: "bool", 23 | }, 24 | ], 25 | stateMutability: "view", 26 | type: "function", 27 | }, 28 | ]; 29 | class IERC165__factory { 30 | static createInterface() { 31 | return new ethers_1.utils.Interface(_abi); 32 | } 33 | static connect(address, signerOrProvider) { 34 | return new ethers_1.Contract(address, _abi, signerOrProvider); 35 | } 36 | } 37 | exports.IERC165__factory = IERC165__factory; 38 | IERC165__factory.abi = _abi; 39 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/IERC165__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { IERC165, IERC165Interface } from "../IERC165"; 8 | 9 | const _abi = [ 10 | { 11 | inputs: [ 12 | { 13 | internalType: "bytes4", 14 | name: "interfaceId", 15 | type: "bytes4", 16 | }, 17 | ], 18 | name: "supportsInterface", 19 | outputs: [ 20 | { 21 | internalType: "bool", 22 | name: "", 23 | type: "bool", 24 | }, 25 | ], 26 | stateMutability: "view", 27 | type: "function", 28 | }, 29 | ] as const; 30 | 31 | export class IERC165__factory { 32 | static readonly abi = _abi; 33 | static createInterface(): IERC165Interface { 34 | return new utils.Interface(_abi) as IERC165Interface; 35 | } 36 | static connect( 37 | address: string, 38 | signerOrProvider: Signer | Provider 39 | ): IERC165 { 40 | return new Contract(address, _abi, signerOrProvider) as IERC165; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/IERC20__factory.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* Autogenerated file. Do not edit manually. */ 3 | /* tslint:disable */ 4 | /* eslint-disable */ 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.IERC20__factory = void 0; 7 | const ethers_1 = require("ethers"); 8 | const _abi = [ 9 | { 10 | anonymous: false, 11 | inputs: [ 12 | { 13 | indexed: true, 14 | internalType: "address", 15 | name: "owner", 16 | type: "address", 17 | }, 18 | { 19 | indexed: true, 20 | internalType: "address", 21 | name: "spender", 22 | type: "address", 23 | }, 24 | { 25 | indexed: false, 26 | internalType: "uint256", 27 | name: "value", 28 | type: "uint256", 29 | }, 30 | ], 31 | name: "Approval", 32 | type: "event", 33 | }, 34 | { 35 | anonymous: false, 36 | inputs: [ 37 | { 38 | indexed: true, 39 | internalType: "address", 40 | name: "from", 41 | type: "address", 42 | }, 43 | { 44 | indexed: true, 45 | internalType: "address", 46 | name: "to", 47 | type: "address", 48 | }, 49 | { 50 | indexed: false, 51 | internalType: "uint256", 52 | name: "value", 53 | type: "uint256", 54 | }, 55 | ], 56 | name: "Transfer", 57 | type: "event", 58 | }, 59 | { 60 | inputs: [ 61 | { 62 | internalType: "address", 63 | name: "owner", 64 | type: "address", 65 | }, 66 | { 67 | internalType: "address", 68 | name: "spender", 69 | type: "address", 70 | }, 71 | ], 72 | name: "allowance", 73 | outputs: [ 74 | { 75 | internalType: "uint256", 76 | name: "", 77 | type: "uint256", 78 | }, 79 | ], 80 | stateMutability: "view", 81 | type: "function", 82 | }, 83 | { 84 | inputs: [ 85 | { 86 | internalType: "address", 87 | name: "spender", 88 | type: "address", 89 | }, 90 | { 91 | internalType: "uint256", 92 | name: "amount", 93 | type: "uint256", 94 | }, 95 | ], 96 | name: "approve", 97 | outputs: [ 98 | { 99 | internalType: "bool", 100 | name: "", 101 | type: "bool", 102 | }, 103 | ], 104 | stateMutability: "nonpayable", 105 | type: "function", 106 | }, 107 | { 108 | inputs: [ 109 | { 110 | internalType: "address", 111 | name: "account", 112 | type: "address", 113 | }, 114 | ], 115 | name: "balanceOf", 116 | outputs: [ 117 | { 118 | internalType: "uint256", 119 | name: "", 120 | type: "uint256", 121 | }, 122 | ], 123 | stateMutability: "view", 124 | type: "function", 125 | }, 126 | { 127 | inputs: [], 128 | name: "totalSupply", 129 | outputs: [ 130 | { 131 | internalType: "uint256", 132 | name: "", 133 | type: "uint256", 134 | }, 135 | ], 136 | stateMutability: "view", 137 | type: "function", 138 | }, 139 | { 140 | inputs: [ 141 | { 142 | internalType: "address", 143 | name: "to", 144 | type: "address", 145 | }, 146 | { 147 | internalType: "uint256", 148 | name: "amount", 149 | type: "uint256", 150 | }, 151 | ], 152 | name: "transfer", 153 | outputs: [ 154 | { 155 | internalType: "bool", 156 | name: "", 157 | type: "bool", 158 | }, 159 | ], 160 | stateMutability: "nonpayable", 161 | type: "function", 162 | }, 163 | { 164 | inputs: [ 165 | { 166 | internalType: "address", 167 | name: "from", 168 | type: "address", 169 | }, 170 | { 171 | internalType: "address", 172 | name: "to", 173 | type: "address", 174 | }, 175 | { 176 | internalType: "uint256", 177 | name: "amount", 178 | type: "uint256", 179 | }, 180 | ], 181 | name: "transferFrom", 182 | outputs: [ 183 | { 184 | internalType: "bool", 185 | name: "", 186 | type: "bool", 187 | }, 188 | ], 189 | stateMutability: "nonpayable", 190 | type: "function", 191 | }, 192 | ]; 193 | class IERC20__factory { 194 | static createInterface() { 195 | return new ethers_1.utils.Interface(_abi); 196 | } 197 | static connect(address, signerOrProvider) { 198 | return new ethers_1.Contract(address, _abi, signerOrProvider); 199 | } 200 | } 201 | exports.IERC20__factory = IERC20__factory; 202 | IERC20__factory.abi = _abi; 203 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/IERC20__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { IERC20, IERC20Interface } from "../IERC20"; 8 | 9 | const _abi = [ 10 | { 11 | anonymous: false, 12 | inputs: [ 13 | { 14 | indexed: true, 15 | internalType: "address", 16 | name: "owner", 17 | type: "address", 18 | }, 19 | { 20 | indexed: true, 21 | internalType: "address", 22 | name: "spender", 23 | type: "address", 24 | }, 25 | { 26 | indexed: false, 27 | internalType: "uint256", 28 | name: "value", 29 | type: "uint256", 30 | }, 31 | ], 32 | name: "Approval", 33 | type: "event", 34 | }, 35 | { 36 | anonymous: false, 37 | inputs: [ 38 | { 39 | indexed: true, 40 | internalType: "address", 41 | name: "from", 42 | type: "address", 43 | }, 44 | { 45 | indexed: true, 46 | internalType: "address", 47 | name: "to", 48 | type: "address", 49 | }, 50 | { 51 | indexed: false, 52 | internalType: "uint256", 53 | name: "value", 54 | type: "uint256", 55 | }, 56 | ], 57 | name: "Transfer", 58 | type: "event", 59 | }, 60 | { 61 | inputs: [ 62 | { 63 | internalType: "address", 64 | name: "owner", 65 | type: "address", 66 | }, 67 | { 68 | internalType: "address", 69 | name: "spender", 70 | type: "address", 71 | }, 72 | ], 73 | name: "allowance", 74 | outputs: [ 75 | { 76 | internalType: "uint256", 77 | name: "", 78 | type: "uint256", 79 | }, 80 | ], 81 | stateMutability: "view", 82 | type: "function", 83 | }, 84 | { 85 | inputs: [ 86 | { 87 | internalType: "address", 88 | name: "spender", 89 | type: "address", 90 | }, 91 | { 92 | internalType: "uint256", 93 | name: "amount", 94 | type: "uint256", 95 | }, 96 | ], 97 | name: "approve", 98 | outputs: [ 99 | { 100 | internalType: "bool", 101 | name: "", 102 | type: "bool", 103 | }, 104 | ], 105 | stateMutability: "nonpayable", 106 | type: "function", 107 | }, 108 | { 109 | inputs: [ 110 | { 111 | internalType: "address", 112 | name: "account", 113 | type: "address", 114 | }, 115 | ], 116 | name: "balanceOf", 117 | outputs: [ 118 | { 119 | internalType: "uint256", 120 | name: "", 121 | type: "uint256", 122 | }, 123 | ], 124 | stateMutability: "view", 125 | type: "function", 126 | }, 127 | { 128 | inputs: [], 129 | name: "totalSupply", 130 | outputs: [ 131 | { 132 | internalType: "uint256", 133 | name: "", 134 | type: "uint256", 135 | }, 136 | ], 137 | stateMutability: "view", 138 | type: "function", 139 | }, 140 | { 141 | inputs: [ 142 | { 143 | internalType: "address", 144 | name: "to", 145 | type: "address", 146 | }, 147 | { 148 | internalType: "uint256", 149 | name: "amount", 150 | type: "uint256", 151 | }, 152 | ], 153 | name: "transfer", 154 | outputs: [ 155 | { 156 | internalType: "bool", 157 | name: "", 158 | type: "bool", 159 | }, 160 | ], 161 | stateMutability: "nonpayable", 162 | type: "function", 163 | }, 164 | { 165 | inputs: [ 166 | { 167 | internalType: "address", 168 | name: "from", 169 | type: "address", 170 | }, 171 | { 172 | internalType: "address", 173 | name: "to", 174 | type: "address", 175 | }, 176 | { 177 | internalType: "uint256", 178 | name: "amount", 179 | type: "uint256", 180 | }, 181 | ], 182 | name: "transferFrom", 183 | outputs: [ 184 | { 185 | internalType: "bool", 186 | name: "", 187 | type: "bool", 188 | }, 189 | ], 190 | stateMutability: "nonpayable", 191 | type: "function", 192 | }, 193 | ] as const; 194 | 195 | export class IERC20__factory { 196 | static readonly abi = _abi; 197 | static createInterface(): IERC20Interface { 198 | return new utils.Interface(_abi) as IERC20Interface; 199 | } 200 | static connect(address: string, signerOrProvider: Signer | Provider): IERC20 { 201 | return new Contract(address, _abi, signerOrProvider) as IERC20; 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/IERC2981__factory.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* Autogenerated file. Do not edit manually. */ 3 | /* tslint:disable */ 4 | /* eslint-disable */ 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.IERC2981__factory = void 0; 7 | const ethers_1 = require("ethers"); 8 | const _abi = [ 9 | { 10 | inputs: [ 11 | { 12 | internalType: "uint256", 13 | name: "tokenId", 14 | type: "uint256", 15 | }, 16 | { 17 | internalType: "uint256", 18 | name: "salePrice", 19 | type: "uint256", 20 | }, 21 | ], 22 | name: "royaltyInfo", 23 | outputs: [ 24 | { 25 | internalType: "address", 26 | name: "receiver", 27 | type: "address", 28 | }, 29 | { 30 | internalType: "uint256", 31 | name: "royaltyAmount", 32 | type: "uint256", 33 | }, 34 | ], 35 | stateMutability: "view", 36 | type: "function", 37 | }, 38 | { 39 | inputs: [ 40 | { 41 | internalType: "bytes4", 42 | name: "interfaceId", 43 | type: "bytes4", 44 | }, 45 | ], 46 | name: "supportsInterface", 47 | outputs: [ 48 | { 49 | internalType: "bool", 50 | name: "", 51 | type: "bool", 52 | }, 53 | ], 54 | stateMutability: "view", 55 | type: "function", 56 | }, 57 | ]; 58 | class IERC2981__factory { 59 | static createInterface() { 60 | return new ethers_1.utils.Interface(_abi); 61 | } 62 | static connect(address, signerOrProvider) { 63 | return new ethers_1.Contract(address, _abi, signerOrProvider); 64 | } 65 | } 66 | exports.IERC2981__factory = IERC2981__factory; 67 | IERC2981__factory.abi = _abi; 68 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/IERC2981__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { IERC2981, IERC2981Interface } from "../IERC2981"; 8 | 9 | const _abi = [ 10 | { 11 | inputs: [ 12 | { 13 | internalType: "uint256", 14 | name: "tokenId", 15 | type: "uint256", 16 | }, 17 | { 18 | internalType: "uint256", 19 | name: "salePrice", 20 | type: "uint256", 21 | }, 22 | ], 23 | name: "royaltyInfo", 24 | outputs: [ 25 | { 26 | internalType: "address", 27 | name: "receiver", 28 | type: "address", 29 | }, 30 | { 31 | internalType: "uint256", 32 | name: "royaltyAmount", 33 | type: "uint256", 34 | }, 35 | ], 36 | stateMutability: "view", 37 | type: "function", 38 | }, 39 | { 40 | inputs: [ 41 | { 42 | internalType: "bytes4", 43 | name: "interfaceId", 44 | type: "bytes4", 45 | }, 46 | ], 47 | name: "supportsInterface", 48 | outputs: [ 49 | { 50 | internalType: "bool", 51 | name: "", 52 | type: "bool", 53 | }, 54 | ], 55 | stateMutability: "view", 56 | type: "function", 57 | }, 58 | ] as const; 59 | 60 | export class IERC2981__factory { 61 | static readonly abi = _abi; 62 | static createInterface(): IERC2981Interface { 63 | return new utils.Interface(_abi) as IERC2981Interface; 64 | } 65 | static connect( 66 | address: string, 67 | signerOrProvider: Signer | Provider 68 | ): IERC2981 { 69 | return new Contract(address, _abi, signerOrProvider) as IERC2981; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/IERC721A__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { IERC721A, IERC721AInterface } from "../IERC721A"; 8 | 9 | const _abi = [ 10 | { 11 | inputs: [], 12 | name: "ApprovalCallerNotOwnerNorApproved", 13 | type: "error", 14 | }, 15 | { 16 | inputs: [], 17 | name: "ApprovalQueryForNonexistentToken", 18 | type: "error", 19 | }, 20 | { 21 | inputs: [], 22 | name: "ApproveToCaller", 23 | type: "error", 24 | }, 25 | { 26 | inputs: [], 27 | name: "BalanceQueryForZeroAddress", 28 | type: "error", 29 | }, 30 | { 31 | inputs: [], 32 | name: "MintToZeroAddress", 33 | type: "error", 34 | }, 35 | { 36 | inputs: [], 37 | name: "MintZeroQuantity", 38 | type: "error", 39 | }, 40 | { 41 | inputs: [], 42 | name: "OwnerQueryForNonexistentToken", 43 | type: "error", 44 | }, 45 | { 46 | inputs: [], 47 | name: "TransferCallerNotOwnerNorApproved", 48 | type: "error", 49 | }, 50 | { 51 | inputs: [], 52 | name: "TransferFromIncorrectOwner", 53 | type: "error", 54 | }, 55 | { 56 | inputs: [], 57 | name: "TransferToNonERC721ReceiverImplementer", 58 | type: "error", 59 | }, 60 | { 61 | inputs: [], 62 | name: "TransferToZeroAddress", 63 | type: "error", 64 | }, 65 | { 66 | inputs: [], 67 | name: "URIQueryForNonexistentToken", 68 | type: "error", 69 | }, 70 | { 71 | anonymous: false, 72 | inputs: [ 73 | { 74 | indexed: true, 75 | internalType: "address", 76 | name: "owner", 77 | type: "address", 78 | }, 79 | { 80 | indexed: true, 81 | internalType: "address", 82 | name: "approved", 83 | type: "address", 84 | }, 85 | { 86 | indexed: true, 87 | internalType: "uint256", 88 | name: "tokenId", 89 | type: "uint256", 90 | }, 91 | ], 92 | name: "Approval", 93 | type: "event", 94 | }, 95 | { 96 | anonymous: false, 97 | inputs: [ 98 | { 99 | indexed: true, 100 | internalType: "address", 101 | name: "owner", 102 | type: "address", 103 | }, 104 | { 105 | indexed: true, 106 | internalType: "address", 107 | name: "operator", 108 | type: "address", 109 | }, 110 | { 111 | indexed: false, 112 | internalType: "bool", 113 | name: "approved", 114 | type: "bool", 115 | }, 116 | ], 117 | name: "ApprovalForAll", 118 | type: "event", 119 | }, 120 | { 121 | anonymous: false, 122 | inputs: [ 123 | { 124 | indexed: true, 125 | internalType: "address", 126 | name: "from", 127 | type: "address", 128 | }, 129 | { 130 | indexed: true, 131 | internalType: "address", 132 | name: "to", 133 | type: "address", 134 | }, 135 | { 136 | indexed: true, 137 | internalType: "uint256", 138 | name: "tokenId", 139 | type: "uint256", 140 | }, 141 | ], 142 | name: "Transfer", 143 | type: "event", 144 | }, 145 | { 146 | inputs: [ 147 | { 148 | internalType: "address", 149 | name: "to", 150 | type: "address", 151 | }, 152 | { 153 | internalType: "uint256", 154 | name: "tokenId", 155 | type: "uint256", 156 | }, 157 | ], 158 | name: "approve", 159 | outputs: [], 160 | stateMutability: "nonpayable", 161 | type: "function", 162 | }, 163 | { 164 | inputs: [ 165 | { 166 | internalType: "address", 167 | name: "owner", 168 | type: "address", 169 | }, 170 | ], 171 | name: "balanceOf", 172 | outputs: [ 173 | { 174 | internalType: "uint256", 175 | name: "balance", 176 | type: "uint256", 177 | }, 178 | ], 179 | stateMutability: "view", 180 | type: "function", 181 | }, 182 | { 183 | inputs: [ 184 | { 185 | internalType: "uint256", 186 | name: "tokenId", 187 | type: "uint256", 188 | }, 189 | ], 190 | name: "getApproved", 191 | outputs: [ 192 | { 193 | internalType: "address", 194 | name: "operator", 195 | type: "address", 196 | }, 197 | ], 198 | stateMutability: "view", 199 | type: "function", 200 | }, 201 | { 202 | inputs: [ 203 | { 204 | internalType: "address", 205 | name: "owner", 206 | type: "address", 207 | }, 208 | { 209 | internalType: "address", 210 | name: "operator", 211 | type: "address", 212 | }, 213 | ], 214 | name: "isApprovedForAll", 215 | outputs: [ 216 | { 217 | internalType: "bool", 218 | name: "", 219 | type: "bool", 220 | }, 221 | ], 222 | stateMutability: "view", 223 | type: "function", 224 | }, 225 | { 226 | inputs: [], 227 | name: "name", 228 | outputs: [ 229 | { 230 | internalType: "string", 231 | name: "", 232 | type: "string", 233 | }, 234 | ], 235 | stateMutability: "view", 236 | type: "function", 237 | }, 238 | { 239 | inputs: [ 240 | { 241 | internalType: "uint256", 242 | name: "tokenId", 243 | type: "uint256", 244 | }, 245 | ], 246 | name: "ownerOf", 247 | outputs: [ 248 | { 249 | internalType: "address", 250 | name: "owner", 251 | type: "address", 252 | }, 253 | ], 254 | stateMutability: "view", 255 | type: "function", 256 | }, 257 | { 258 | inputs: [ 259 | { 260 | internalType: "address", 261 | name: "from", 262 | type: "address", 263 | }, 264 | { 265 | internalType: "address", 266 | name: "to", 267 | type: "address", 268 | }, 269 | { 270 | internalType: "uint256", 271 | name: "tokenId", 272 | type: "uint256", 273 | }, 274 | ], 275 | name: "safeTransferFrom", 276 | outputs: [], 277 | stateMutability: "nonpayable", 278 | type: "function", 279 | }, 280 | { 281 | inputs: [ 282 | { 283 | internalType: "address", 284 | name: "from", 285 | type: "address", 286 | }, 287 | { 288 | internalType: "address", 289 | name: "to", 290 | type: "address", 291 | }, 292 | { 293 | internalType: "uint256", 294 | name: "tokenId", 295 | type: "uint256", 296 | }, 297 | { 298 | internalType: "bytes", 299 | name: "data", 300 | type: "bytes", 301 | }, 302 | ], 303 | name: "safeTransferFrom", 304 | outputs: [], 305 | stateMutability: "nonpayable", 306 | type: "function", 307 | }, 308 | { 309 | inputs: [ 310 | { 311 | internalType: "address", 312 | name: "operator", 313 | type: "address", 314 | }, 315 | { 316 | internalType: "bool", 317 | name: "_approved", 318 | type: "bool", 319 | }, 320 | ], 321 | name: "setApprovalForAll", 322 | outputs: [], 323 | stateMutability: "nonpayable", 324 | type: "function", 325 | }, 326 | { 327 | inputs: [ 328 | { 329 | internalType: "bytes4", 330 | name: "interfaceId", 331 | type: "bytes4", 332 | }, 333 | ], 334 | name: "supportsInterface", 335 | outputs: [ 336 | { 337 | internalType: "bool", 338 | name: "", 339 | type: "bool", 340 | }, 341 | ], 342 | stateMutability: "view", 343 | type: "function", 344 | }, 345 | { 346 | inputs: [], 347 | name: "symbol", 348 | outputs: [ 349 | { 350 | internalType: "string", 351 | name: "", 352 | type: "string", 353 | }, 354 | ], 355 | stateMutability: "view", 356 | type: "function", 357 | }, 358 | { 359 | inputs: [ 360 | { 361 | internalType: "uint256", 362 | name: "tokenId", 363 | type: "uint256", 364 | }, 365 | ], 366 | name: "tokenURI", 367 | outputs: [ 368 | { 369 | internalType: "string", 370 | name: "", 371 | type: "string", 372 | }, 373 | ], 374 | stateMutability: "view", 375 | type: "function", 376 | }, 377 | { 378 | inputs: [], 379 | name: "totalSupply", 380 | outputs: [ 381 | { 382 | internalType: "uint256", 383 | name: "", 384 | type: "uint256", 385 | }, 386 | ], 387 | stateMutability: "view", 388 | type: "function", 389 | }, 390 | { 391 | inputs: [ 392 | { 393 | internalType: "address", 394 | name: "from", 395 | type: "address", 396 | }, 397 | { 398 | internalType: "address", 399 | name: "to", 400 | type: "address", 401 | }, 402 | { 403 | internalType: "uint256", 404 | name: "tokenId", 405 | type: "uint256", 406 | }, 407 | ], 408 | name: "transferFrom", 409 | outputs: [], 410 | stateMutability: "nonpayable", 411 | type: "function", 412 | }, 413 | ] as const; 414 | 415 | export class IERC721A__factory { 416 | static readonly abi = _abi; 417 | static createInterface(): IERC721AInterface { 418 | return new utils.Interface(_abi) as IERC721AInterface; 419 | } 420 | static connect( 421 | address: string, 422 | signerOrProvider: Signer | Provider 423 | ): IERC721A { 424 | return new Contract(address, _abi, signerOrProvider) as IERC721A; 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/IRenderer__factory.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* Autogenerated file. Do not edit manually. */ 3 | /* tslint:disable */ 4 | /* eslint-disable */ 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.IRenderer__factory = void 0; 7 | const ethers_1 = require("ethers"); 8 | const _abi = [ 9 | { 10 | inputs: [ 11 | { 12 | internalType: "uint256", 13 | name: "tokenId", 14 | type: "uint256", 15 | }, 16 | ], 17 | name: "tokenURI", 18 | outputs: [ 19 | { 20 | internalType: "string", 21 | name: "", 22 | type: "string", 23 | }, 24 | ], 25 | stateMutability: "view", 26 | type: "function", 27 | }, 28 | ]; 29 | class IRenderer__factory { 30 | static createInterface() { 31 | return new ethers_1.utils.Interface(_abi); 32 | } 33 | static connect(address, signerOrProvider) { 34 | return new ethers_1.Contract(address, _abi, signerOrProvider); 35 | } 36 | } 37 | exports.IRenderer__factory = IRenderer__factory; 38 | IRenderer__factory.abi = _abi; 39 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/IRenderer__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { IRenderer, IRendererInterface } from "../IRenderer"; 8 | 9 | const _abi = [ 10 | { 11 | inputs: [ 12 | { 13 | internalType: "uint256", 14 | name: "tokenId", 15 | type: "uint256", 16 | }, 17 | ], 18 | name: "tokenURI", 19 | outputs: [ 20 | { 21 | internalType: "string", 22 | name: "", 23 | type: "string", 24 | }, 25 | ], 26 | stateMutability: "view", 27 | type: "function", 28 | }, 29 | ] as const; 30 | 31 | export class IRenderer__factory { 32 | static readonly abi = _abi; 33 | static createInterface(): IRendererInterface { 34 | return new utils.Interface(_abi) as IRendererInterface; 35 | } 36 | static connect( 37 | address: string, 38 | signerOrProvider: Signer | Provider 39 | ): IRenderer { 40 | return new Contract(address, _abi, signerOrProvider) as IRenderer; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/Ownable__factory.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | /* Autogenerated file. Do not edit manually. */ 3 | /* tslint:disable */ 4 | /* eslint-disable */ 5 | Object.defineProperty(exports, "__esModule", { value: true }); 6 | exports.Ownable__factory = void 0; 7 | const ethers_1 = require("ethers"); 8 | const _abi = [ 9 | { 10 | anonymous: false, 11 | inputs: [ 12 | { 13 | indexed: true, 14 | internalType: "address", 15 | name: "previousOwner", 16 | type: "address", 17 | }, 18 | { 19 | indexed: true, 20 | internalType: "address", 21 | name: "newOwner", 22 | type: "address", 23 | }, 24 | ], 25 | name: "OwnershipTransferred", 26 | type: "event", 27 | }, 28 | { 29 | inputs: [], 30 | name: "owner", 31 | outputs: [ 32 | { 33 | internalType: "address", 34 | name: "", 35 | type: "address", 36 | }, 37 | ], 38 | stateMutability: "view", 39 | type: "function", 40 | }, 41 | { 42 | inputs: [], 43 | name: "renounceOwnership", 44 | outputs: [], 45 | stateMutability: "nonpayable", 46 | type: "function", 47 | }, 48 | { 49 | inputs: [ 50 | { 51 | internalType: "address", 52 | name: "newOwner", 53 | type: "address", 54 | }, 55 | ], 56 | name: "transferOwnership", 57 | outputs: [], 58 | stateMutability: "nonpayable", 59 | type: "function", 60 | }, 61 | ]; 62 | class Ownable__factory { 63 | static createInterface() { 64 | return new ethers_1.utils.Interface(_abi); 65 | } 66 | static connect(address, signerOrProvider) { 67 | return new ethers_1.Contract(address, _abi, signerOrProvider); 68 | } 69 | } 70 | exports.Ownable__factory = Ownable__factory; 71 | Ownable__factory.abi = _abi; 72 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/Ownable__factory.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | 5 | import { Contract, Signer, utils } from "ethers"; 6 | import type { Provider } from "@ethersproject/providers"; 7 | import type { Ownable, OwnableInterface } from "../Ownable"; 8 | 9 | const _abi = [ 10 | { 11 | anonymous: false, 12 | inputs: [ 13 | { 14 | indexed: true, 15 | internalType: "address", 16 | name: "previousOwner", 17 | type: "address", 18 | }, 19 | { 20 | indexed: true, 21 | internalType: "address", 22 | name: "newOwner", 23 | type: "address", 24 | }, 25 | ], 26 | name: "OwnershipTransferred", 27 | type: "event", 28 | }, 29 | { 30 | inputs: [], 31 | name: "owner", 32 | outputs: [ 33 | { 34 | internalType: "address", 35 | name: "", 36 | type: "address", 37 | }, 38 | ], 39 | stateMutability: "view", 40 | type: "function", 41 | }, 42 | { 43 | inputs: [], 44 | name: "renounceOwnership", 45 | outputs: [], 46 | stateMutability: "nonpayable", 47 | type: "function", 48 | }, 49 | { 50 | inputs: [ 51 | { 52 | internalType: "address", 53 | name: "newOwner", 54 | type: "address", 55 | }, 56 | ], 57 | name: "transferOwnership", 58 | outputs: [], 59 | stateMutability: "nonpayable", 60 | type: "function", 61 | }, 62 | ] as const; 63 | 64 | export class Ownable__factory { 65 | static readonly abi = _abi; 66 | static createInterface(): OwnableInterface { 67 | return new utils.Interface(_abi) as OwnableInterface; 68 | } 69 | static connect( 70 | address: string, 71 | signerOrProvider: Signer | Provider 72 | ): Ownable { 73 | return new Contract(address, _abi, signerOrProvider) as Ownable; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | Object.defineProperty(exports, "__esModule", { value: true }); 26 | exports.Ownable__factory = exports.IRenderer__factory = exports.IERC721A__factory = exports.IERC2981__factory = exports.IERC20__factory = exports.IERC165__factory = exports.ExampleNFT__factory = exports.ERC721Base__factory = exports.erc721ASol = void 0; 27 | /* Autogenerated file. Do not edit manually. */ 28 | /* tslint:disable */ 29 | /* eslint-disable */ 30 | exports.erc721ASol = __importStar(require("./ERC721A.sol")); 31 | var ERC721Base__factory_1 = require("./ERC721Base__factory"); 32 | Object.defineProperty(exports, "ERC721Base__factory", { enumerable: true, get: function () { return ERC721Base__factory_1.ERC721Base__factory; } }); 33 | var ExampleNFT__factory_1 = require("./ExampleNFT__factory"); 34 | Object.defineProperty(exports, "ExampleNFT__factory", { enumerable: true, get: function () { return ExampleNFT__factory_1.ExampleNFT__factory; } }); 35 | var IERC165__factory_1 = require("./IERC165__factory"); 36 | Object.defineProperty(exports, "IERC165__factory", { enumerable: true, get: function () { return IERC165__factory_1.IERC165__factory; } }); 37 | var IERC20__factory_1 = require("./IERC20__factory"); 38 | Object.defineProperty(exports, "IERC20__factory", { enumerable: true, get: function () { return IERC20__factory_1.IERC20__factory; } }); 39 | var IERC2981__factory_1 = require("./IERC2981__factory"); 40 | Object.defineProperty(exports, "IERC2981__factory", { enumerable: true, get: function () { return IERC2981__factory_1.IERC2981__factory; } }); 41 | var IERC721A__factory_1 = require("./IERC721A__factory"); 42 | Object.defineProperty(exports, "IERC721A__factory", { enumerable: true, get: function () { return IERC721A__factory_1.IERC721A__factory; } }); 43 | var IRenderer__factory_1 = require("./IRenderer__factory"); 44 | Object.defineProperty(exports, "IRenderer__factory", { enumerable: true, get: function () { return IRenderer__factory_1.IRenderer__factory; } }); 45 | var Ownable__factory_1 = require("./Ownable__factory"); 46 | Object.defineProperty(exports, "Ownable__factory", { enumerable: true, get: function () { return Ownable__factory_1.Ownable__factory; } }); 47 | -------------------------------------------------------------------------------- /packages/contracts/types/factories/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | export * as erc721ASol from "./ERC721A.sol"; 5 | export { ERC721Base__factory } from "./ERC721Base__factory"; 6 | export { ExampleNFT__factory } from "./ExampleNFT__factory"; 7 | export { IERC165__factory } from "./IERC165__factory"; 8 | export { IERC20__factory } from "./IERC20__factory"; 9 | export { IERC2981__factory } from "./IERC2981__factory"; 10 | export { IERC721A__factory } from "./IERC721A__factory"; 11 | export { IRenderer__factory } from "./IRenderer__factory"; 12 | export { Ownable__factory } from "./Ownable__factory"; 13 | -------------------------------------------------------------------------------- /packages/contracts/types/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | Object.defineProperty(exports, "__esModule", { value: true }); 26 | exports.Ownable__factory = exports.IRenderer__factory = exports.IERC721A__factory = exports.IERC2981__factory = exports.IERC20__factory = exports.IERC165__factory = exports.ExampleNFT__factory = exports.ERC721Base__factory = exports.ERC721A__factory = exports.ERC721A__IERC721Receiver__factory = exports.factories = void 0; 27 | exports.factories = __importStar(require("./factories")); 28 | var ERC721A__IERC721Receiver__factory_1 = require("./factories/ERC721A.sol/ERC721A__IERC721Receiver__factory"); 29 | Object.defineProperty(exports, "ERC721A__IERC721Receiver__factory", { enumerable: true, get: function () { return ERC721A__IERC721Receiver__factory_1.ERC721A__IERC721Receiver__factory; } }); 30 | var ERC721A__factory_1 = require("./factories/ERC721A.sol/ERC721A__factory"); 31 | Object.defineProperty(exports, "ERC721A__factory", { enumerable: true, get: function () { return ERC721A__factory_1.ERC721A__factory; } }); 32 | var ERC721Base__factory_1 = require("./factories/ERC721Base__factory"); 33 | Object.defineProperty(exports, "ERC721Base__factory", { enumerable: true, get: function () { return ERC721Base__factory_1.ERC721Base__factory; } }); 34 | var ExampleNFT__factory_1 = require("./factories/ExampleNFT__factory"); 35 | Object.defineProperty(exports, "ExampleNFT__factory", { enumerable: true, get: function () { return ExampleNFT__factory_1.ExampleNFT__factory; } }); 36 | var IERC165__factory_1 = require("./factories/IERC165__factory"); 37 | Object.defineProperty(exports, "IERC165__factory", { enumerable: true, get: function () { return IERC165__factory_1.IERC165__factory; } }); 38 | var IERC20__factory_1 = require("./factories/IERC20__factory"); 39 | Object.defineProperty(exports, "IERC20__factory", { enumerable: true, get: function () { return IERC20__factory_1.IERC20__factory; } }); 40 | var IERC2981__factory_1 = require("./factories/IERC2981__factory"); 41 | Object.defineProperty(exports, "IERC2981__factory", { enumerable: true, get: function () { return IERC2981__factory_1.IERC2981__factory; } }); 42 | var IERC721A__factory_1 = require("./factories/IERC721A__factory"); 43 | Object.defineProperty(exports, "IERC721A__factory", { enumerable: true, get: function () { return IERC721A__factory_1.IERC721A__factory; } }); 44 | var IRenderer__factory_1 = require("./factories/IRenderer__factory"); 45 | Object.defineProperty(exports, "IRenderer__factory", { enumerable: true, get: function () { return IRenderer__factory_1.IRenderer__factory; } }); 46 | var Ownable__factory_1 = require("./factories/Ownable__factory"); 47 | Object.defineProperty(exports, "Ownable__factory", { enumerable: true, get: function () { return Ownable__factory_1.Ownable__factory; } }); 48 | -------------------------------------------------------------------------------- /packages/contracts/types/index.ts: -------------------------------------------------------------------------------- 1 | /* Autogenerated file. Do not edit manually. */ 2 | /* tslint:disable */ 3 | /* eslint-disable */ 4 | import type * as erc721ASol from "./ERC721A.sol"; 5 | export type { erc721ASol }; 6 | export type { ERC721Base } from "./ERC721Base"; 7 | export type { ExampleNFT } from "./ExampleNFT"; 8 | export type { IERC165 } from "./IERC165"; 9 | export type { IERC20 } from "./IERC20"; 10 | export type { IERC2981 } from "./IERC2981"; 11 | export type { IERC721A } from "./IERC721A"; 12 | export type { IRenderer } from "./IRenderer"; 13 | export type { Ownable } from "./Ownable"; 14 | export * as factories from "./factories"; 15 | export type { ERC721A__IERC721Receiver } from "./ERC721A.sol/ERC721A__IERC721Receiver"; 16 | export { ERC721A__IERC721Receiver__factory } from "./factories/ERC721A.sol/ERC721A__IERC721Receiver__factory"; 17 | export type { ERC721A } from "./ERC721A.sol/ERC721A"; 18 | export { ERC721A__factory } from "./factories/ERC721A.sol/ERC721A__factory"; 19 | export { ERC721Base__factory } from "./factories/ERC721Base__factory"; 20 | export { ExampleNFT__factory } from "./factories/ExampleNFT__factory"; 21 | export { IERC165__factory } from "./factories/IERC165__factory"; 22 | export { IERC20__factory } from "./factories/IERC20__factory"; 23 | export { IERC2981__factory } from "./factories/IERC2981__factory"; 24 | export { IERC721A__factory } from "./factories/IERC721A__factory"; 25 | export { IRenderer__factory } from "./factories/IRenderer__factory"; 26 | export { Ownable__factory } from "./factories/Ownable__factory"; 27 | -------------------------------------------------------------------------------- /packages/subgraph/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | generated 4 | build -------------------------------------------------------------------------------- /packages/subgraph/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@web3-scaffold/subgraph", 3 | "private": true, 4 | "engines": { 5 | "node": "16", 6 | "pnpm": "7" 7 | }, 8 | "scripts": { 9 | "codegen": "graph codegen subgraph*.yaml", 10 | "build": "pnpm codegen && graph build subgraph*.yaml", 11 | "deploy:goerli": "graph deploy --node https://api.thegraph.com/deploy/ holic/example-nft subgraph-goerli.yaml", 12 | "prettier": "prettier --write src", 13 | "lint": "eslint src" 14 | }, 15 | "dependencies": { 16 | "@graphprotocol/graph-cli": "0.30.4", 17 | "@graphprotocol/graph-ts": "0.27.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/subgraph/schema.graphql: -------------------------------------------------------------------------------- 1 | type Token @entity { 2 | id: ID! 3 | owner: Bytes! 4 | tokenURI: String! 5 | } 6 | -------------------------------------------------------------------------------- /packages/subgraph/src/mapping.ts: -------------------------------------------------------------------------------- 1 | import { ExampleNFT, Transfer } from "../generated/ExampleNFT/ExampleNFT"; 2 | import { Token } from "../generated/schema"; 3 | 4 | export function handleTransfer(event: Transfer): void { 5 | const contract = ExampleNFT.bind(event.address); 6 | 7 | const token = new Token(event.params.tokenId.toString()); 8 | token.owner = event.params.to; 9 | token.tokenURI = contract.tokenURI(event.params.tokenId); 10 | token.save(); 11 | } 12 | -------------------------------------------------------------------------------- /packages/subgraph/subgraph-goerli.yaml: -------------------------------------------------------------------------------- 1 | specVersion: 0.0.4 2 | schema: 3 | file: ./schema.graphql 4 | dataSources: 5 | - kind: ethereum 6 | name: ExampleNFT 7 | network: goerli 8 | source: 9 | abi: ExampleNFT 10 | address: "0xe584409f2ba1ade9895485d90587fd46baa3c0d8" 11 | startBlock: 7034594 12 | mapping: 13 | kind: ethereum/events 14 | apiVersion: 0.0.5 15 | language: wasm/assemblyscript 16 | entities: 17 | - NFT 18 | abis: 19 | - name: ExampleNFT 20 | file: ../contracts/out/ExampleNFT.sol/ExampleNFT.abi.json 21 | eventHandlers: 22 | - event: Transfer(indexed address,indexed address,indexed uint256) 23 | handler: handleTransfer 24 | file: ./src/mapping.ts 25 | -------------------------------------------------------------------------------- /packages/subgraph/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@graphprotocol/graph-ts/types/tsconfig.base.json", 3 | "include": ["src"] 4 | } 5 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* 3 | -------------------------------------------------------------------------------- /remappings.txt: -------------------------------------------------------------------------------- 1 | forge-std/=packages/contracts/lib/forge-std/src/ 2 | @openzeppelin/=packages/contracts/lib/openzeppelin-contracts/ 3 | erc721a/=packages/contracts/lib/ERC721A/ 4 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { 3 | "silent": true 4 | } 5 | } 6 | --------------------------------------------------------------------------------