From c944c51b2f39e1bdb9203e3c7311c44849334b9b Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Wed, 10 Jan 2024 20:56:49 -0800 Subject: [PATCH] change nft color based on build count --- README.md | 11 +- .../{BuidlGuidlNft.sol => BuidlCountNft.sol} | 63 +- packages/foundry/deployments/1.json | 3 + packages/foundry/deployments/31337.json | 3 + packages/foundry/package.json | 2 +- packages/foundry/script/Deploy.s.sol | 6 +- packages/foundry/svg/BuidlGuidl.svg | 13 - packages/foundry/svg/logo.svg | 16 + packages/foundry/svg/nft.svg | 20 + packages/foundry/svg/radialGradient.svg | 9 + packages/foundry/svg/rainbow.svg | 14 + packages/foundry/svg/yellowToRed.svg | 9 + packages/nextjs/components/Header.tsx | 20 +- .../scaffold-eth/Contract/ContractUI.tsx | 8 +- .../Contract/ReadOnlyFunctionForm.tsx | 4 +- .../components/scaffold-eth/FaucetButton.tsx | 2 +- .../AddressInfoDropdown.tsx | 2 +- .../nextjs/contracts/deployedContracts.ts | 1162 ++++++++++++++++- packages/nextjs/next.config.js | 3 + packages/nextjs/pages/index.tsx | 2 +- packages/nextjs/styles/globals.css | 2 +- packages/nextjs/tailwind.config.js | 34 +- 22 files changed, 1324 insertions(+), 84 deletions(-) rename packages/foundry/contracts/{BuidlGuidlNft.sol => BuidlCountNft.sol} (70%) create mode 100644 packages/foundry/deployments/1.json create mode 100644 packages/foundry/deployments/31337.json delete mode 100644 packages/foundry/svg/BuidlGuidl.svg create mode 100644 packages/foundry/svg/logo.svg create mode 100644 packages/foundry/svg/nft.svg create mode 100644 packages/foundry/svg/radialGradient.svg create mode 100644 packages/foundry/svg/rainbow.svg create mode 100644 packages/foundry/svg/yellowToRed.svg diff --git a/README.md b/README.md index 4f1428f..378974e 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,18 @@ 1. Impliment chainlink function to fetch build count from BG api 2. Set up minting on frontend with mainnet ens lookup that is passed as arg to mint function 3. Hook up subgraph for speedy display of the nft collection -4. Figure out if account abstraction is possible for the minting to pay users mint fees -5. Ship it +4. Different color background depending on number of builds + 1. 0 - 4 builds gets bronze + 2. 5 - 9 builds gets silver + 3. 10+ builds gets rainbow or gold +5. Figure out if account abstraction is possible for the minting to pay users mint fees ## Resources +### NFT metadata example + +- ipfs://bafybeibc5sgo2plmjkq2tzmhrn54bk3crhnc23zd2msg4ea7a4pxrkgfna/2222 + ### Subgraphs - https://thegraph.com/docs/en/developing/creating-a-subgraph/#writing-mappings diff --git a/packages/foundry/contracts/BuidlGuidlNft.sol b/packages/foundry/contracts/BuidlCountNft.sol similarity index 70% rename from packages/foundry/contracts/BuidlGuidlNft.sol rename to packages/foundry/contracts/BuidlCountNft.sol index 6c1a35d..672c760 100644 --- a/packages/foundry/contracts/BuidlGuidlNft.sol +++ b/packages/foundry/contracts/BuidlCountNft.sol @@ -7,34 +7,29 @@ import {Base64} from "@openzeppelin/contracts/utils/Base64.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; /** - * Basic setup for dynamic svg nft project - * - * * MemberData.ensName sourced from frontend that queries mainnet ens registry * MemberData.buildCount comes from chainlink function */ -contract BuidlGuidlNft is ERC721 { +contract BuidlCountNft is ERC721 { struct MemberData { string ensName; uint8 buildCount; } mapping(address => MemberData) public s_memberToData; + mapping(address => bool) private hasMinted; uint256 private s_tokenCounter; string private constant base64EncodedSvgPrefix = "data:image/svg+xml;base64,"; - string[] private s_backgroundColors = [ - "#4f46e5", - "#ef4444", - "#10b981", - "#3b82f6", - "#f59e0b" - ]; + + string public zeroToFourBuilds = "#52525b"; + string public fiveToNineBuilds = "#2563eb"; + string public tenPlusBuilds = "#4f46e5"; /** * First NFT minted has tokenId of 0 */ - constructor() ERC721("Buidl Guidl", "BGDL") { + constructor() ERC721("Buidl Counter", "BDLC") { s_tokenCounter = 0; } @@ -45,13 +40,20 @@ contract BuidlGuidlNft is ERC721 { function svgToImageURI( uint256 tokenId ) public view returns (string memory) { - uint256 colorIndex = tokenId % s_backgroundColors.length; - string memory color = s_backgroundColors[colorIndex]; - string memory svgPart1 = buildSvgPart1(color); - string memory svgPart2 = buildSvgPart2(color, tokenId); + address ownerAddr = ownerOf(tokenId); + string memory color; + if (s_memberToData[ownerAddr].buildCount < 5) { + color = zeroToFourBuilds; + } else if (s_memberToData[ownerAddr].buildCount < 10) { + color = fiveToNineBuilds; + } else { + color = tenPlusBuilds; + } + string memory svgTop = buildSvgTop(color); + string memory svgBottom = buildSvgBottom(color, tokenId); string memory svgBase64Encoded = Base64.encode( - bytes(string(abi.encodePacked(svgPart1, svgPart2))) + bytes(string(abi.encodePacked(svgTop, svgBottom))) ); return string(abi.encodePacked(base64EncodedSvgPrefix, svgBase64Encoded)); @@ -63,7 +65,7 @@ contract BuidlGuidlNft is ERC721 { * @param backgroundColor used to fill background of svg and text of ensName */ - function buildSvgPart1( + function buildSvgTop( string memory backgroundColor ) private pure returns (string memory) { return @@ -73,10 +75,11 @@ contract BuidlGuidlNft is ERC721 { '', - '', - '', + '', + '', + '', "" - 'BuidlGuidl', + 'BuidlGuidl', '' ) ); @@ -87,13 +90,13 @@ contract BuidlGuidlNft is ERC721 { * @param textColor text color for buildCount number (matches the background color) * @param tokenId to get the address of the NFT owner used to lookup dynamic ens name and buildCount */ - function buildSvgPart2( + function buildSvgBottom( string memory textColor, uint256 tokenId ) private view returns (string memory) { address ownerAddr = ownerOf(tokenId); - string memory buildCount = Strings.toString( + string memory buildCountString = Strings.toString( s_memberToData[ownerAddr].buildCount ); string memory ensName = s_memberToData[ownerAddr].ensName; @@ -133,7 +136,7 @@ contract BuidlGuidlNft is ERC721 { '', - buildCount, + buildCountString, "", 'Builds Shipped', "" @@ -147,8 +150,14 @@ contract BuidlGuidlNft is ERC721 { * 3. mint new NFT * */ - function minNft() public { + function minNft(uint8 _buidlCount) public { + require(!hasMinted[msg.sender], "Address has already minted an NFT"); + s_memberToData[msg.sender] = MemberData({ + ensName: "", + buildCount: uint8(_buidlCount) + }); _safeMint(msg.sender, s_tokenCounter); + hasMinted[msg.sender] = true; s_tokenCounter++; } @@ -180,4 +189,8 @@ contract BuidlGuidlNft is ERC721 { ) ); } + + function getBuidlCount(address _memberAddr) public view returns (uint8) { + return s_memberToData[_memberAddr].buildCount; + } } diff --git a/packages/foundry/deployments/1.json b/packages/foundry/deployments/1.json new file mode 100644 index 0000000..9e9e8d8 --- /dev/null +++ b/packages/foundry/deployments/1.json @@ -0,0 +1,3 @@ +{ + "networkName": "Mainnet" +} \ No newline at end of file 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/package.json b/packages/foundry/package.json index 9471ed9..cc9fa1d 100644 --- a/packages/foundry/package.json +++ b/packages/foundry/package.json @@ -7,7 +7,7 @@ "compile": "forge compile", "deploy": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast --legacy && node script/generateTsAbis.js", "deploy:verify": "forge build --build-info --build-info-path out/build-info/ && forge script script/Deploy.s.sol --rpc-url ${1:-default_network} --broadcast --legacy --verify ; node script/generateTsAbis.js", - "fork": "anvil --fork-url ${0:-mainnet} --chain-id 31337 --config-out localhost.json", + "fork": "anvil --fork-url $MAINNET_RPC_URL --chain-id 31337 --config-out localhost.json", "generate": "node script/generateAccount.js", "lint": "forge fmt", "test": "forge test", diff --git a/packages/foundry/script/Deploy.s.sol b/packages/foundry/script/Deploy.s.sol index cd73590..ca327e0 100644 --- a/packages/foundry/script/Deploy.s.sol +++ b/packages/foundry/script/Deploy.s.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.19; import "./DeployHelpers.s.sol"; -import {BuidlGuidlNft} from "../contracts/BuidlGuidlNft.sol"; +import {BuidlCountNft} from "../contracts/BuidlCountNft.sol"; contract DeployScript is ScaffoldETHDeploy { error InvalidPrivateKey(string); @@ -16,11 +16,11 @@ contract DeployScript is ScaffoldETHDeploy { } vm.startBroadcast(deployerPrivateKey); - BuidlGuidlNft bgNft = new BuidlGuidlNft(); + BuidlCountNft buidlCountNft = new BuidlCountNft(); console.logString( string.concat( "bgNft contract deployed at: ", - vm.toString(address(bgNft)) + vm.toString(address(buidlCountNft)) ) ); vm.stopBroadcast(); diff --git a/packages/foundry/svg/BuidlGuidl.svg b/packages/foundry/svg/BuidlGuidl.svg deleted file mode 100644 index 89d0b86..0000000 --- a/packages/foundry/svg/BuidlGuidl.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - BuidlGuidl - - matthu.eth - 0x41f727fA294E50400aC27317832A9F78659476B9 - - 8 - Builds Shipped - diff --git a/packages/foundry/svg/logo.svg b/packages/foundry/svg/logo.svg new file mode 100644 index 0000000..3e826f2 --- /dev/null +++ b/packages/foundry/svg/logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/packages/foundry/svg/nft.svg b/packages/foundry/svg/nft.svg new file mode 100644 index 0000000..9d935bd --- /dev/null +++ b/packages/foundry/svg/nft.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + BuidlGuidl + + matthu.eth + + + 8 + Builds Shipped + diff --git a/packages/foundry/svg/radialGradient.svg b/packages/foundry/svg/radialGradient.svg new file mode 100644 index 0000000..999a891 --- /dev/null +++ b/packages/foundry/svg/radialGradient.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/foundry/svg/rainbow.svg b/packages/foundry/svg/rainbow.svg new file mode 100644 index 0000000..1996062 --- /dev/null +++ b/packages/foundry/svg/rainbow.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/packages/foundry/svg/yellowToRed.svg b/packages/foundry/svg/yellowToRed.svg new file mode 100644 index 0000000..aa9d613 --- /dev/null +++ b/packages/foundry/svg/yellowToRed.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/nextjs/components/Header.tsx b/packages/nextjs/components/Header.tsx index 4a5d86b..b9ca3fc 100644 --- a/packages/nextjs/components/Header.tsx +++ b/packages/nextjs/components/Header.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useRef, useState } from "react"; import Image from "next/image"; import Link from "next/link"; -import { useRouter } from "next/router"; +// import { useRouter } from "next/router"; import { Bars3Icon, BugAntIcon } from "@heroicons/react/24/outline"; import { FaucetButton, RainbowKitCustomConnectButton } from "~~/components/scaffold-eth"; import { useOutsideClick } from "~~/hooks/scaffold-eth"; @@ -25,20 +25,19 @@ export const menuLinks: HeaderMenuLink[] = [ ]; export const HeaderMenuLinks = () => { - const router = useRouter(); + // const router = useRouter(); return ( <> {menuLinks.map(({ label, href, icon }) => { - const isActive = router.pathname === href; + // const isActive = router.pathname === href; return (
  • {icon} {label} @@ -62,7 +61,7 @@ export const Header = () => { ); return ( -
    +