Skip to content

Commit

Permalink
fleshed out UI and integrated tiering system
Browse files Browse the repository at this point in the history
  • Loading branch information
JacobHomanics committed Feb 23, 2024
1 parent d4c725c commit 9402922
Show file tree
Hide file tree
Showing 12 changed files with 1,029 additions and 76 deletions.
5 changes: 5 additions & 0 deletions images/ATXP.json
Original file line number Diff line number Diff line change
@@ -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"
}
Binary file added images/ATXP.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/PP1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/PP2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 44 additions & 18 deletions packages/foundry/contracts/ATXDAOPartnershipNft.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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;
}
}
3 changes: 3 additions & 0 deletions packages/foundry/deployments/31337.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"networkName": "Anvil"
}
8 changes: 6 additions & 2 deletions packages/foundry/script/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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(
Expand Down
21 changes: 12 additions & 9 deletions packages/foundry/test/YourContract.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
Expand All @@ -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();
Expand All @@ -71,27 +74,27 @@ 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);
}

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);
Expand Down
104 changes: 58 additions & 46 deletions packages/nextjs/app/page.tsx
Original file line number Diff line number Diff line change
@@ -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) => (
<NftCard key={index} nft={metadata}/>
));


async function onFormSubmit(event: any) {
event.preventDefault()
await mint({args: [event.target[0].value, BigInt(0) ]});
}

let adminOutput;
if (hasAdminRole) {
adminOutput = <div className="flex items-center justify-center">
<form onSubmit={onFormSubmit}>
<div className="flex flex-col items-center justify-center space-y-1">
<label htmlFor="recipient" className="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Recipient</label>
<input type="text" id="recipient" className="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"/>
<button type="submit" className="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800">Mint NFT</button>
</div>
</form></div>
}

return (
<>
<div className="flex items-center flex-col flex-grow pt-10">
<div className="px-5">
<h1 className="text-center mb-8">
<span className="block text-2xl mb-2">Welcome to</span>
<span className="block text-4xl font-bold">Scaffold-ETH 2</span>
</h1>
<p className="text-center text-lg">
Get started by editing{" "}
<code className="italic bg-base-300 text-base font-bold max-w-full break-words break-all inline-block">
packages/nextjs/app/page.tsx
</code>
</p>
<p className="text-center text-lg">
Edit your smart contract{" "}
<code className="italic bg-base-300 text-base font-bold max-w-full break-words break-all inline-block">
YourContract.sol
</code>{" "}
in{" "}
<code className="italic bg-base-300 text-base font-bold max-w-full break-words break-all inline-block">
packages/hardhat/contracts
</code>
</p>
</div>

<div className="flex-grow bg-base-300 w-full mt-16 px-8 py-12">
<div className="flex justify-center items-center gap-12 flex-col sm:flex-row">
<div className="flex flex-col bg-base-100 px-10 py-10 text-center items-center max-w-xs rounded-3xl">
<BugAntIcon className="h-8 w-8 fill-secondary" />
<p>
Tinker with your smart contract using the{" "}
<Link href="/debug" passHref className="link">
Debug Contract
</Link>{" "}
tab.
</p>
</div>
<div className="flex flex-col bg-base-100 px-10 py-10 text-center items-center max-w-xs rounded-3xl">
<MagnifyingGlassIcon className="h-8 w-8 fill-secondary" />
<p>
Explore your local transactions with the{" "}
<Link href="/blockexplorer" passHref className="link">
Block Explorer
</Link>{" "}
tab.
</p>
</div>
</div>
{
adminOutput
}
<div className="grid grid-cols-2 space-x-1 space-y-1">
{
nfts
}
</div>
</div>
</>
Expand Down
46 changes: 46 additions & 0 deletions packages/nextjs/components/nft-card/Hooks.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useEffect, useState } from "react";

export function useGetAllMetadatas(contract: any, mintCount: bigint) {
const [data, setData] = useState<any[]>([]);

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<any[]>([]);

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 };
}
24 changes: 24 additions & 0 deletions packages/nextjs/components/nft-card/nftCard.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex flex-col items-center justify-center bg-black rounded">
<p>Name: { props.nft.name}</p>
<p>Description: { props.nft.description}</p>
<img src={props.nft.image} width={126} height={126}/>
</div>
)
}
Loading

0 comments on commit 9402922

Please sign in to comment.