From 9402922e71c795354793ecb0e322f12ee5919ba0 Mon Sep 17 00:00:00 2001 From: Jacob Homanics Date: Fri, 23 Feb 2024 05:55:50 -0600 Subject: [PATCH] fleshed out UI and integrated tiering system --- images/ATXP.json | 5 + images/ATXP.png | Bin 0 -> 8136 bytes images/PP1.png | Bin 0 -> 8426 bytes images/PP2.png | Bin 0 -> 9142 bytes .../contracts/ATXDAOPartnershipNft.sol | 62 +- packages/foundry/deployments/31337.json | 3 + packages/foundry/script/Deploy.s.sol | 8 +- packages/foundry/test/YourContract.t.sol | 21 +- packages/nextjs/app/page.tsx | 104 ++- packages/nextjs/components/nft-card/Hooks.tsx | 46 + .../nextjs/components/nft-card/nftCard.tsx | 24 + .../nextjs/contracts/deployedContracts.ts | 832 +++++++++++++++++- 12 files changed, 1029 insertions(+), 76 deletions(-) create mode 100644 images/ATXP.json create mode 100644 images/ATXP.png create mode 100644 images/PP1.png create mode 100644 images/PP2.png create mode 100644 packages/foundry/deployments/31337.json create mode 100644 packages/nextjs/components/nft-card/Hooks.tsx create mode 100644 packages/nextjs/components/nft-card/nftCard.tsx diff --git a/images/ATXP.json b/images/ATXP.json new file mode 100644 index 0000000..6210545 --- /dev/null +++ b/images/ATXP.json @@ -0,0 +1,5 @@ +{ + "name": "ATX DAO Partnership NFT", + "description": "This NFT represents an outstanding partner with ATX DAO. If you are the owner, then upload a json file to IPFS that follows the NFT metadata standard and update this NFT's tokenURI!", + "image": "ipfs://bafkreicxupdmf55oaivaf26de3r62kzw4lgmjigfswzlkxo4yk6lf67b2a" +} \ No newline at end of file diff --git a/images/ATXP.png b/images/ATXP.png new file mode 100644 index 0000000000000000000000000000000000000000..3554e58fac56bbfa719de5ff46b06d133d602522 GIT binary patch literal 8136 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 literal 0 HcmV?d00001 diff --git a/images/PP1.png b/images/PP1.png new file mode 100644 index 0000000000000000000000000000000000000000..cae0021819db6364b408d6d08daa097007c421d0 GIT binary patch literal 8426 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 literal 0 HcmV?d00001 diff --git a/images/PP2.png b/images/PP2.png new file mode 100644 index 0000000000000000000000000000000000000000..261673314a15e373db46afc1d0b21e683c29f387 GIT binary patch literal 9142 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 literal 0 HcmV?d00001 diff --git a/packages/foundry/contracts/ATXDAOPartnershipNft.sol b/packages/foundry/contracts/ATXDAOPartnershipNft.sol index f01a420..11e8c2e 100644 --- a/packages/foundry/contracts/ATXDAOPartnershipNft.sol +++ b/packages/foundry/contracts/ATXDAOPartnershipNft.sol @@ -8,21 +8,49 @@ import {ERC721URIStorage} from "@openzeppelin/contracts/token/ERC721/extensions/ import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; contract ATXDAOPartnershipNft is ERC721URIStorage, AccessControl { - constructor(address[] memory admins) ERC721("ADNDN", "ATX") { + error ATXDAOPartnershipNft__NotValidOperator(); + + string mintUri; + uint256 mintCount; + uint256 tier; + + constructor( + address[] memory admins, + string memory newMintUri + ) ERC721("ATX DAO Partnership NFT", "ATXP") { for (uint256 i = 0; i < admins.length; i++) { _grantRole(DEFAULT_ADMIN_ROLE, admins[i]); } + + mintUri = newMintUri; } - uint256 mintCount; + function _checkAuthorized( + address owner, + address spender, + uint256 tokenId + ) internal view override { + if (!hasRole(DEFAULT_ADMIN_ROLE, spender)) { + super._checkAuthorized(owner, spender, tokenId); + } + } - function mint(address target) external onlyRole(DEFAULT_ADMIN_ROLE) { + function supportsInterface( + bytes4 interfaceId + ) public view override(ERC721URIStorage, AccessControl) returns (bool) { + return super.supportsInterface(interfaceId); + } + + function mint( + address target, + uint256 newTier + ) external onlyRole(DEFAULT_ADMIN_ROLE) { _mint(target, mintCount); + _setTokenURI(mintCount, mintUri); mintCount++; + tier = newTier; } - error ATXDAOPartnershipNft__NotValidOperator(); - function setTokenURI(uint256 tokenId, string memory tokenUri) external { if (msg.sender != ownerOf(tokenId)) { if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) { @@ -33,26 +61,24 @@ contract ATXDAOPartnershipNft is ERC721URIStorage, AccessControl { _setTokenURI(tokenId, tokenUri); } - function returnToDao( + function revokeNft( uint256 tokenId, address receiver ) external onlyRole(DEFAULT_ADMIN_ROLE) { transferFrom(ownerOf(tokenId), receiver, tokenId); } - function _checkAuthorized( - address owner, - address spender, - uint256 tokenId - ) internal view override { - if (!hasRole(DEFAULT_ADMIN_ROLE, spender)) { - super._checkAuthorized(owner, spender, tokenId); - } + function setMintUri( + string memory newMintUri + ) external onlyRole(DEFAULT_ADMIN_ROLE) { + mintUri = newMintUri; } - function supportsInterface( - bytes4 interfaceId - ) public view override(ERC721URIStorage, AccessControl) returns (bool) { - return super.supportsInterface(interfaceId); + function getMintUri() external view returns (string memory) { + return mintUri; + } + + function getMintCount() external view returns (uint256) { + return mintCount; } } diff --git a/packages/foundry/deployments/31337.json b/packages/foundry/deployments/31337.json new file mode 100644 index 0000000..3d482af --- /dev/null +++ b/packages/foundry/deployments/31337.json @@ -0,0 +1,3 @@ +{ + "networkName": "Anvil" +} \ No newline at end of file diff --git a/packages/foundry/script/Deploy.s.sol b/packages/foundry/script/Deploy.s.sol index e4ecc51..cb94530 100644 --- a/packages/foundry/script/Deploy.s.sol +++ b/packages/foundry/script/Deploy.s.sol @@ -7,7 +7,7 @@ import "./DeployHelpers.s.sol"; contract DeployScript is ScaffoldETHDeploy { error InvalidPrivateKey(string); - address admin = makeAddr("Admin"); + address admin = 0x62286D694F89a1B12c0214bfcD567bb6c2951491; function run() external { uint256 deployerPrivateKey = setupLocalhostEnv(); @@ -16,12 +16,16 @@ contract DeployScript is ScaffoldETHDeploy { "You don't have a deployer account. Make sure you have set DEPLOYER_PRIVATE_KEY in .env or use `yarn generate` to generate a new random account" ); } + vm.startBroadcast(deployerPrivateKey); address[] memory admins = new address[](1); admins[0] = admin; - ATXDAOPartnershipNft yourContract = new ATXDAOPartnershipNft(admins); + ATXDAOPartnershipNft yourContract = new ATXDAOPartnershipNft( + admins, + "ipfs://bafkreide5gtpol2fzt75qt5rpds5vdmv24qnf43frionfhesfqqb2en66a" + ); console.logString( string.concat( diff --git a/packages/foundry/test/YourContract.t.sol b/packages/foundry/test/YourContract.t.sol index 20ef0db..0806555 100644 --- a/packages/foundry/test/YourContract.t.sol +++ b/packages/foundry/test/YourContract.t.sol @@ -20,12 +20,15 @@ contract YourContractTest is Test { address[] memory admins = new address[](1); admins[0] = admin; - nftCollection = new ATXDAOPartnershipNft(admins); + nftCollection = new ATXDAOPartnershipNft( + admins, + "ipfs://bafkreide5gtpol2fzt75qt5rpds5vdmv24qnf43frionfhesfqqb2en66a" + ); } function testSetTokenUriAsOwner() public { vm.prank(admin); - nftCollection.mint(user); + nftCollection.mint(user, 0); vm.prank(user); nftCollection.setTokenURI( @@ -41,7 +44,7 @@ contract YourContractTest is Test { function testSetTokenUriAsAdmin() public { vm.prank(admin); - nftCollection.mint(user); + nftCollection.mint(user, 0); vm.startPrank(admin); nftCollection.setTokenURI( @@ -58,7 +61,7 @@ contract YourContractTest is Test { function testRevertIfNotOwner() public { vm.prank(admin); - nftCollection.mint(user); + nftCollection.mint(user, 0); vm.startPrank(user2); vm.expectRevert(); @@ -71,9 +74,9 @@ contract YourContractTest is Test { function testReturnToDao() public { vm.startPrank(admin); - nftCollection.mint(user); + nftCollection.mint(user, 0); - nftCollection.returnToDao(0, multisig); + nftCollection.revokeNft(0, multisig); vm.stopPrank(); assertEq(nftCollection.ownerOf(0), multisig); @@ -81,17 +84,17 @@ contract YourContractTest is Test { function testRevert__ReturnToDao__IfNotAdmin() public { vm.startPrank(admin); - nftCollection.mint(user); + nftCollection.mint(user, 0); vm.stopPrank(); vm.prank(user); vm.expectRevert(); - nftCollection.returnToDao(0, multisig); + nftCollection.revokeNft(0, multisig); } function testTransfer() public { vm.startPrank(admin); - nftCollection.mint(user); + nftCollection.mint(user, 0); vm.stopPrank(); vm.prank(user); diff --git a/packages/nextjs/app/page.tsx b/packages/nextjs/app/page.tsx index 035b331..0890f73 100644 --- a/packages/nextjs/app/page.tsx +++ b/packages/nextjs/app/page.tsx @@ -1,57 +1,69 @@ +"use client"; + import Link from "next/link"; import type { NextPage } from "next"; import { BugAntIcon, MagnifyingGlassIcon } from "@heroicons/react/24/outline"; +import { NftCard } from "~~/components/nft-card/nftCard"; +import { useScaffoldContract, useScaffoldContractRead, useScaffoldContractWrite } from "~~/hooks/scaffold-eth"; +import { useAccount } from "wagmi"; +import { FormEvent } from 'react' +import { useFetches, useGetAllMetadatas } from "~~/components/nft-card/Hooks"; const Home: NextPage = () => { + const account = useAccount(); + + const { data: partnershipNftContract } = useScaffoldContract({contractName: "ATXDAOPartnershipNft"}); + + const { data: mintCount } = useScaffoldContractRead({contractName:"ATXDAOPartnershipNft", functionName:"getMintCount"}); + + const { data: adminRole } = useScaffoldContractRead({contractName:"ATXDAOPartnershipNft", functionName: "DEFAULT_ADMIN_ROLE"}); + + const { data: hasAdminRole } = useScaffoldContractRead({contractName:"ATXDAOPartnershipNft", functionName: "hasRole", args: [adminRole, account.address]}); + + const { writeAsync: mint } = useScaffoldContractWrite({contractName: "ATXDAOPartnershipNft", functionName: "mint", args: ["", BigInt(0)] }); + + + const { data: tokenURIs } = useGetAllMetadatas(partnershipNftContract, mintCount || BigInt(0)); + + for (let i = 0; i < tokenURIs.length; i++) { + tokenURIs[i] = tokenURIs[i].replace("ipfs://", "https://ipfs.io/ipfs/"); + } + + const { data: metadatas } = useFetches(tokenURIs); + + + const nfts = metadatas!.map((metadata, index) => ( + + )); + + + async function onFormSubmit(event: any) { + event.preventDefault() + await mint({args: [event.target[0].value, BigInt(0) ]}); + } + + let adminOutput; + if (hasAdminRole) { + adminOutput =
+
+
+ + + +
+
+ } + return ( <>
-
-

- Welcome to - Scaffold-ETH 2 -

-

- Get started by editing{" "} - - packages/nextjs/app/page.tsx - -

-

- Edit your smart contract{" "} - - YourContract.sol - {" "} - in{" "} - - packages/hardhat/contracts - -

-
- -
-
-
- -

- Tinker with your smart contract using the{" "} - - Debug Contract - {" "} - tab. -

-
-
- -

- Explore your local transactions with the{" "} - - Block Explorer - {" "} - tab. -

-
-
+ { + adminOutput + } +
+ { + nfts + }
diff --git a/packages/nextjs/components/nft-card/Hooks.tsx b/packages/nextjs/components/nft-card/Hooks.tsx new file mode 100644 index 0000000..5eba9b3 --- /dev/null +++ b/packages/nextjs/components/nft-card/Hooks.tsx @@ -0,0 +1,46 @@ +import { useEffect, useState } from "react"; + +export function useGetAllMetadatas(contract: any, mintCount: bigint) { + const [data, setData] = useState([]); + + useEffect(()=> { + async function get() { + const arr = []; + for (let i = 0; i < mintCount; i++) { + let result = await contract?.read.tokenURI([i]); + arr.push(result); + } + + setData([...arr]); + } + get(); + }, [contract?.address, mintCount]) + + return { data } +} + +export function useFetches(uris: string[]) { + const [data, setData] = useState([]); + + useEffect(() => { + async function get() { + const arr = []; + for (let i = 0; i < uris.length; i++) { + try { + const response = await fetch(uris[i]); + const responseJson = await response.json(); + arr.push(responseJson); + } + catch (e) { + console.log('Could not fetch URI'); + arr.push({}); + } + } + + setData([...arr]); + } + get(); + }, [uris]); + + return { data }; + } \ No newline at end of file diff --git a/packages/nextjs/components/nft-card/nftCard.tsx b/packages/nextjs/components/nft-card/nftCard.tsx new file mode 100644 index 0000000..31a3b5a --- /dev/null +++ b/packages/nextjs/components/nft-card/nftCard.tsx @@ -0,0 +1,24 @@ +interface Nft { + name?: string; + description?: string; + image?: string; +} + +interface NftCardProps { + nft: Nft; +} + +export function NftCard (props: NftCardProps) { + + if (props.nft && props.nft.image){ + props.nft.image = props.nft.image.replace("ipfs://", "https://ipfs.io/ipfs/"); + } + + return ( +
+

Name: { props.nft.name}

+

Description: { props.nft.description}

+ +
+ ) +} \ No newline at end of file diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 008d4eb..c1d880e 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -4,6 +4,836 @@ */ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; -const deployedContracts = {} as const; +const deployedContracts = { + 31337: { + ATXDAOPartnershipNft: { + address: "0x0165878A594ca255338adfa4d48449f69242Eb8F", + abi: [ + { + type: "constructor", + inputs: [ + { + name: "admins", + type: "address[]", + internalType: "address[]", + }, + { + name: "newMintUri", + type: "string", + internalType: "string", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "DEFAULT_ADMIN_ROLE", + inputs: [], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "approve", + inputs: [ + { + name: "to", + type: "address", + internalType: "address", + }, + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "balanceOf", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getApproved", + inputs: [ + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getMintCount", + inputs: [], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getMintUri", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getRoleAdmin", + inputs: [ + { + name: "role", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "grantRole", + inputs: [ + { + name: "role", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "hasRole", + inputs: [ + { + name: "role", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "isApprovedForAll", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "operator", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "mint", + inputs: [ + { + name: "target", + type: "address", + internalType: "address", + }, + { + name: "newTier", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "name", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "ownerOf", + inputs: [ + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "renounceRole", + inputs: [ + { + name: "role", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "callerConfirmation", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "revokeNft", + inputs: [ + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + { + name: "receiver", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "revokeRole", + inputs: [ + { + name: "role", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "safeTransferFrom", + inputs: [ + { + name: "from", + type: "address", + internalType: "address", + }, + { + name: "to", + type: "address", + internalType: "address", + }, + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "safeTransferFrom", + inputs: [ + { + name: "from", + type: "address", + internalType: "address", + }, + { + name: "to", + type: "address", + internalType: "address", + }, + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + { + name: "data", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setApprovalForAll", + inputs: [ + { + name: "operator", + type: "address", + internalType: "address", + }, + { + name: "approved", + type: "bool", + internalType: "bool", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setMintUri", + inputs: [ + { + name: "newMintUri", + type: "string", + internalType: "string", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setTokenURI", + inputs: [ + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + { + name: "tokenUri", + type: "string", + internalType: "string", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "supportsInterface", + inputs: [ + { + name: "interfaceId", + type: "bytes4", + internalType: "bytes4", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "symbol", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "tokenURI", + inputs: [ + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "transferFrom", + inputs: [ + { + name: "from", + type: "address", + internalType: "address", + }, + { + name: "to", + type: "address", + internalType: "address", + }, + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "Approval", + inputs: [ + { + name: "owner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "approved", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "tokenId", + type: "uint256", + indexed: true, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ApprovalForAll", + inputs: [ + { + name: "owner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "operator", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "approved", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "BatchMetadataUpdate", + inputs: [ + { + name: "_fromTokenId", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "_toTokenId", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "MetadataUpdate", + inputs: [ + { + name: "_tokenId", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "RoleAdminChanged", + inputs: [ + { + name: "role", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + { + name: "previousAdminRole", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + { + name: "newAdminRole", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "RoleGranted", + inputs: [ + { + name: "role", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "sender", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "RoleRevoked", + inputs: [ + { + name: "role", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + { + name: "account", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "sender", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Transfer", + inputs: [ + { + name: "from", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "to", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "tokenId", + type: "uint256", + indexed: true, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "ATXDAOPartnershipNft__NotValidOperator", + inputs: [], + }, + { + type: "error", + name: "AccessControlBadConfirmation", + inputs: [], + }, + { + type: "error", + name: "AccessControlUnauthorizedAccount", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + { + name: "neededRole", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + { + type: "error", + name: "ERC721IncorrectOwner", + inputs: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC721InsufficientApproval", + inputs: [ + { + name: "operator", + type: "address", + internalType: "address", + }, + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "ERC721InvalidApprover", + inputs: [ + { + name: "approver", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC721InvalidOperator", + inputs: [ + { + name: "operator", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC721InvalidOwner", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC721InvalidReceiver", + inputs: [ + { + name: "receiver", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC721InvalidSender", + inputs: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC721NonexistentToken", + inputs: [ + { + name: "tokenId", + type: "uint256", + internalType: "uint256", + }, + ], + }, + ], + inheritedFunctions: { + approve: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + balanceOf: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + getApproved: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + isApprovedForAll: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + name: "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + ownerOf: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + safeTransferFrom: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + setApprovalForAll: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + supportsInterface: + "lib/openzeppelin-contracts/contracts/access/AccessControl.sol", + symbol: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + tokenURI: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + transferFrom: + "lib/openzeppelin-contracts/contracts/token/ERC721/extensions/ERC721URIStorage.sol", + DEFAULT_ADMIN_ROLE: + "lib/openzeppelin-contracts/contracts/access/AccessControl.sol", + getRoleAdmin: + "lib/openzeppelin-contracts/contracts/access/AccessControl.sol", + grantRole: + "lib/openzeppelin-contracts/contracts/access/AccessControl.sol", + hasRole: + "lib/openzeppelin-contracts/contracts/access/AccessControl.sol", + renounceRole: + "lib/openzeppelin-contracts/contracts/access/AccessControl.sol", + revokeRole: + "lib/openzeppelin-contracts/contracts/access/AccessControl.sol", + }, + }, + }, +} as const; export default deployedContracts satisfies GenericContractsDeclaration;