diff --git a/contracts/InstrumentDeedToken.sol b/contracts/InstrumentDeedToken.sol index 4c3d837..7128092 100644 --- a/contracts/InstrumentDeedToken.sol +++ b/contracts/InstrumentDeedToken.sol @@ -30,7 +30,7 @@ contract InstrumentDeedToken is ERC721, ERC721Enumerable, ERC721URIStorage, Owna return tokenId; } - // TEMPORARY FUNCTION + // TESTING FUNCTION, DELETE function batchMint(uint _amount, string memory hash) public { for(uint i = 0; i<= _amount; i++){ diff --git a/hardhat.config.js b/hardhat.config.js index 975a2f1..afb0ed8 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -1,7 +1,7 @@ require("@nomiclabs/hardhat-waffle"); require("@nomiclabs/hardhat-ethers"); require("hardhat-gas-reporter"); -require('dotenv').config() +require("dotenv").config(); task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { const accounts = await hre.ethers.getSigners(); @@ -14,14 +14,14 @@ task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { module.exports = { gasReporter: { enabled: true, - currency: 'USD' + currency: "USD", }, paths: { - sources: './contracts' + sources: "./contracts", }, networks: { hardhat: { - chainId: 31337 + chainId: 31337, }, /* kovan: { @@ -33,18 +33,14 @@ module.exports = { accounts:[`${process.env.REACT_APP_PRIVATE_KEY}`], } */ - - - }, + }, solidity: { - version: '0.8.10' - , + version: "0.8.10", settings: { optimizer: { enabled: true, runs: 200, - } - } - } + }, + }, + }, }; - \ No newline at end of file diff --git a/lib/getPathsAndProps.js b/lib/getPathsAndProps.js index 4372f8e..6c3672f 100644 --- a/lib/getPathsAndProps.js +++ b/lib/getPathsAndProps.js @@ -1,43 +1,41 @@ import { useItemContext } from "../src/context/ItemContext"; //ethers -import { ethers } from 'ethers'; +import { ethers } from "ethers"; //abi -import Provenance from '../artifacts/contracts/Provenance.sol/Provenance.json'; +import Provenance from "../artifacts/contracts/Provenance.sol/Provenance.json"; // this needs to be rewritten for non-user specific data only export async function getProvenanceAddresses() { - const { MothershipContract } = useContractObjectRepo() + const { MothershipContract } = useContractObjectRepo(); // const {mainAccount, setMainAccount, signer, provider} = useHandleEthereum(); - - - const addressArray = await MothershipContract.getOwnersInstruments() + const addressArray = await MothershipContract.getOwnersInstruments(); - const paths = addressArray.map((address) => ( - { params: { provenanceAddress: address.toString() }, - })) - - return { paths, fallback: false } - } + const paths = addressArray.map((address) => ({ + params: { provenanceAddress: address.toString() }, + })); + return { paths, fallback: false }; +} - export function getProvenanceProps(context) { - const { provenanceObjects } = useItemContext(); - - const ProvenanceFullProps = []; - - provenanceObjects.map((array, index) => { - const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo } = array; - ProvenanceFullProps.push({ ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo }) - }) +export function getProvenanceProps(context) { + const { provenanceObjects } = useItemContext(); + const ProvenanceFullProps = []; - return { - provAddress, - ProvenanceFullProps - } - } - + provenanceObjects.map((array, index) => { + const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo } = array; + ProvenanceFullProps.push({ + ProvenanceContract, + ProvenanceProps, + ProvenanceOwnerInfo, + }); + }); + return { + provAddress, + ProvenanceFullProps, + }; +} diff --git a/next.config.js b/next.config.js index 1b57365..998ec3a 100644 --- a/next.config.js +++ b/next.config.js @@ -1,9 +1,9 @@ module.exports = { reactStrictMode: true, - react : { - useSuspense: false + react: { + useSuspense: false, }, images: { - domains: ["gateway.pinata.cloud"] + domains: ["gateway.pinata.cloud"], }, -} +}; diff --git a/pages/_app.js b/pages/_app.js index 17f28e5..220d512 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -1,41 +1,33 @@ - -import 'bootstrap/dist/css/bootstrap.css'; -import '../styles/global.css' -import { useEffect } from 'react'; - +import "bootstrap/dist/css/bootstrap.css"; +import "../styles/global.css"; +import { useEffect } from "react"; //temp style imports -import '../src/components/DragAndDrop.css'; - - +import "../src/components/DragAndDrop.css"; //component imports -import NavBar from '../src/components/NavBar/NavBar'; +import NavBar from "../src/components/NavBar/NavBar"; //context imports -import { UserContextProvider} from '../src/context/UserContext'; -import { ContractContextProvider } from '../src/context/ContractContext'; -import { ItemContextProvider } from '../src/context/ItemContext'; -import { TransferContextProvider } from '../src/context/TransferContext'; +import { UserContextProvider } from "../src/context/UserContext"; +import { ContractContextProvider } from "../src/context/ContractContext"; +import { ItemContextProvider } from "../src/context/ItemContext"; +import { TransferContextProvider } from "../src/context/TransferContext"; // SSR Provider -import { SSRProvider } from 'react-bootstrap'; +import { SSRProvider } from "react-bootstrap"; //hook imports // import useHandleEthereum from "../src/hooks/useHandleEthereum.js"; - - function TrackYourAxe({ Component, pageProps }) { - // const {mainAccount, setMainAccount, signer, provider} = useHandleEthereum(); - useEffect(() => { import("bootstrap/dist/js/bootstrap"); }, []); - + return ( <> @@ -45,15 +37,13 @@ function TrackYourAxe({ Component, pageProps }) { - - - - - + + + + + - - - ) + ); } export default TrackYourAxe; diff --git a/pages/about/About.jsx b/pages/about/About.jsx index 44fb954..1face27 100644 --- a/pages/about/About.jsx +++ b/pages/about/About.jsx @@ -1,52 +1,74 @@ import * as React from "react"; -import Link from "next/link"; +import Link from "next/link"; import Image from "next/image"; import { Container } from "react-bootstrap"; -import ethPowerUsage from '../../public/images/eth_power_usage.jpg' -import styles from './About.module.css'; +import ethPowerUsage from "../../public/images/eth_power_usage.jpg"; +import styles from "./About.module.css"; function About() { + return ( + +
+

Track Your...What?

+

+ Track Your Axe is an innovative blockchain-based solution verifying + ownership of musical instruments and items by creating a verifiable + history (provenance) of ownership. +

- return ( - -
-

Track Your...What?

-

- Track Your Axe is an innovative blockchain-based solution verifying ownership of musical instruments and items by creating a verifiable history (provenance) of ownership. -

+

How Does it Work?

+

+ Track Your Axe uses a public, trustless database, i.e. a blockchain, + to publicly store and record ownership of all items listed. This + registry tracks ownership history, item details, item images over + time, and location. +

+
-

How Does it Work?

-

- Track Your Axe uses a public, trustless database, i.e. a blockchain, to publicly store and record ownership of all items listed. This registry tracks ownership history, item details, item images over time, and location. -

-
+

Is that Secure?

+

+ Indeed! To get technical about it, the 'deed' to the Provenance is, + technically, and NFT (non-fungible token). The code used to generate + this NFT is industry-standard and audited by third-parties, so your + ownership of the token is secure. This token is the used as the + indicator of ownership of the provenance itself, which exists at it's + own unique location on the blockchain. +

+

+ The provenance is human-readable on the blockchain (the link to each + contract can be found on your item page) so you can visit it if you + wish, but if you don't wish to go that far, all the information is + displayed on this site. +

-

Is that Secure?

-

- Indeed! To get technical about it, the 'deed' to the Provenance is, technically, and NFT (non-fungible token). The code used to generate this NFT is industry-standard and audited by third-parties, so your ownership of the token is secure. This token is the used as the indicator of ownership of the provenance itself, which exists at it's own unique location on the blockchain. -

-

- The provenance is human-readable on the blockchain (the link to each contract can be found on your item page) so you can visit it if you wish, but if you don't wish to go that far, all the information is displayed on this site. -

+

Why bother? And isn't blockchain bad for the planet?!?

+ +

+ Some instruments are so famous they are a household name; Lucille + comes to mind. However the number of instruments and artifacts that + trade hands daily is legion, and these instruments hold significant + cultural and monetary value. TYA allows for an indestrucible record + and lineage of these items and instruments and their history. +

+

+ Additionally, there are a number of financial incentives to + registering your instrument on TYA. (list insurance, value of horn, + etc){" "} +

+

+ But, really, what about the planet?! and isn't blockchain expensive?! +

+

blah blah blah, yadda yadda

-

Why bother? And isn't blockchain bad for the planet?!?

- -

- Some instruments are so famous they are a household name; Lucille comes to mind. However the number of instruments and artifacts that trade hands daily is legion, and these instruments hold significant cultural and monetary value. TYA allows for - an indestrucible record and lineage of these items and instruments and their history. -

-

Additionally, there are a number of financial incentives to registering your instrument on TYA. (list insurance, value of horn, etc)

-

But, really, what about the planet?! and isn't blockchain expensive?!

-

blah blah blah, yadda yadda

- -

Ok, fine. So how do I register my instruments and items?

-

Excellent question! Visit the tutorial page: here

-
- -
- ); - +

Ok, fine. So how do I register my instruments and items?

+

+ Excellent question! Visit the tutorial page:{" "} + here{" "} +

+
+
+ ); } export default About; diff --git a/pages/about/About.module.css b/pages/about/About.module.css index 5d4c28f..b2e1c59 100644 --- a/pages/about/About.module.css +++ b/pages/about/About.module.css @@ -1,3 +1,3 @@ .bodyText p { - text-align: left; -} \ No newline at end of file + text-align: left; +} diff --git a/pages/about/index.js b/pages/about/index.js index 2aa25a4..3a19a3e 100644 --- a/pages/about/index.js +++ b/pages/about/index.js @@ -1,3 +1,3 @@ -import About from './About.jsx'; +import About from "./About.jsx"; -export default About; \ No newline at end of file +export default About; diff --git a/pages/api/hello.js b/pages/api/hello.js index df63de8..aee21e9 100644 --- a/pages/api/hello.js +++ b/pages/api/hello.js @@ -1,5 +1,5 @@ // Next.js API route support: https://nextjs.org/docs/api-routes/introduction export default function handler(req, res) { - res.status(200).json({ name: 'John Doe' }) + res.status(200).json({ name: "John Doe" }); } diff --git a/pages/guides/how-it-works.js b/pages/guides/how-it-works.js index 390ac7b..67e9d32 100644 --- a/pages/guides/how-it-works.js +++ b/pages/guides/how-it-works.js @@ -1,19 +1,9 @@ function HowItWorksGuide() { - - - return ( - -
- - TEST - -
- - - - - - ); - } - - export default HowItWorksGuide; \ No newline at end of file + return ( +
+ TEST +
+ ); +} + +export default HowItWorksGuide; diff --git a/pages/guides/wallet.js b/pages/guides/wallet.js index e882703..d11eda7 100644 --- a/pages/guides/wallet.js +++ b/pages/guides/wallet.js @@ -1,16 +1,7 @@ -import { useEffect, useState } from 'react'; - - - +import { useEffect, useState } from "react"; function WalletGuide() { + return

earth failed to render

; +} - -return ( -

earth failed to render

-) - - - } - - export default WalletGuide; \ No newline at end of file +export default WalletGuide; diff --git a/pages/incoming-transfers/[incomingTransfer].js b/pages/incoming-transfers/[incomingTransfer].js index af096b9..fd0f5a9 100644 --- a/pages/incoming-transfers/[incomingTransfer].js +++ b/pages/incoming-transfers/[incomingTransfer].js @@ -1,159 +1,181 @@ -import { useEffect, useState, useReducer } from 'react'; -import { ethers } from 'ethers'; +import { useEffect, useState, useReducer } from "react"; +import { ethers } from "ethers"; //next imports -import Link from 'next/link' -import { useRouter } from 'next/router'; +import Link from "next/link"; +import { useRouter } from "next/router"; //react-bootstrap imports -import { Container, Carousel, Table, Row, Col, Card, Image, Button, ListGroup, ListGroupItem} from 'react-bootstrap'; +import { + Container, + Carousel, + Table, + Row, + Col, + Card, + Image, + Button, + ListGroup, + ListGroupItem, +} from "react-bootstrap"; // context imports -import { useItemContext } from '../../src/context/ItemContext'; -import { useContractContext } from '../../src/context/ContractContext'; -import { useUserContext } from '../../src/context/UserContext'; -import { useTransferContext } from '../../src/context/TransferContext'; +import { useItemContext } from "../../src/context/ItemContext"; +import { useContractContext } from "../../src/context/ContractContext"; +import { useUserContext } from "../../src/context/UserContext"; +import { useTransferContext } from "../../src/context/TransferContext"; // component imports -import DragAndDrop from '../../src/components/DragAndDrop.js' -import PhotoPreviews from '../../src/components/PhotoPreviews.js'; - - +import DragAndDrop from "../../src/components/DragAndDrop.js"; +import PhotoPreviews from "../../src/components/PhotoPreviews.js"; // styles -import styles from './transfers.module.css' +import styles from "./transfers.module.css"; const formReducer = (state, event) => { - if(event.reset) { - return { - verificationphotohash: [], - instrumentphotohashes: [], - } - } - + if (event.reset) { return { - ...state, - [event.name]: event.value, - } - - } + verificationphotohash: [], + instrumentphotohashes: [], + }; + } + return { + ...state, + [event.name]: event.value, + }; +}; const TransferProfile = () => { - // contexts const { ipfsGetterRootURL, setItemAdded } = useItemContext(); const { TokenContract, MothershipContract } = useContractContext(); - const { pendingTransferContracts, setPendingTransferContracts } = useTransferContext(); + const { pendingTransferContracts, setPendingTransferContracts } = + useTransferContext(); const { mainAccount, provider, dateString } = useUserContext(); const router = useRouter(); const { incomingTransfer } = router.query; - console.log(router.query) - + console.log(router.query); - const [ loaded , setLoaded ] = useState(false); + const [loaded, setLoaded] = useState(false); // contract info - const [ provenanceContract, setProvenanceContract ] = useState(); - const [ provenanceProps, setProvenanceProps ] = useState(); - const [ provenanceOwnerInfo, setProvenanceOwnerInfo ] = useState(); + const [provenanceContract, setProvenanceContract] = useState(); + const [provenanceProps, setProvenanceProps] = useState(); + const [provenanceOwnerInfo, setProvenanceOwnerInfo] = useState(); - const [formData, setFormData] = useReducer(formReducer, {instrumentphotohashes : [], verificationphotohash : []}); + const [formData, setFormData] = useReducer(formReducer, { + instrumentphotohashes: [], + verificationphotohash: [], + }); - const [buyerAccount, setBuyerAccount] = useState(''); - const [errorMessage, setErrorMessage] = useState(''); - - //load all info + const [buyerAccount, setBuyerAccount] = useState(""); + const [errorMessage, setErrorMessage] = useState(""); - - useEffect(async() => { + //load all info + + useEffect(async () => { if (pendingTransferContracts && incomingTransfer) { - loadProvenance() - } - + loadProvenance(); + } - - async function loadProvenance() { + async function loadProvenance() { for (let contract of pendingTransferContracts) { if (contract.ProvenanceContract.address === incomingTransfer) { - console.log("hitting") + console.log("hitting"); - const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo } = contract; + const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo } = + contract; setProvenanceContract(ProvenanceContract); setProvenanceProps(ProvenanceProps); setProvenanceOwnerInfo(ProvenanceOwnerInfo); - setLoaded(true) + setLoaded(true); } } } - },[pendingTransferContracts, incomingTransfer]) - - - + }, [pendingTransferContracts, incomingTransfer]); function ClaimProvenance() { - console.log((formData.verificationphotohash.length > 1)) + console.log(formData.verificationphotohash.length > 1); async function claim() { - - await provenanceContract.claimOwnership(provenanceOwnerInfo.ownerAddress, formData.verificationphotohash, dateString) - .then(async(result) => { - provider.waitForTransaction(result.hash) - .then(async(mined) => { + await provenanceContract + .claimOwnership( + provenanceOwnerInfo.ownerAddress, + formData.verificationphotohash, + dateString + ) + .then(async (result) => { + provider.waitForTransaction(result.hash).then(async (mined) => { if (mined) { //need to autopush on transaction mine, and make sure new data is pulled to delete pending and add to provObjects - setItemAdded(true); - router.push(`/provenances`); - } - } - ) - } - ) - } - return ( -
- + setItemAdded(true); + router.push(`/provenances`); + } + }); + }); + } + return ( +
+
- - - ) + ); } - console.log(loaded, "loaded check") - - if (loaded) { - - const{ serial, brand, instrumentDeedToken, model, year, typeOfProvenance } = provenanceProps; - - + console.log(loaded, "loaded check"); + + if (loaded) { + const { + serial, + brand, + instrumentDeedToken, + model, + year, + typeOfProvenance, + } = provenanceProps; return ( -
-

Claim Provenance

- +

Claim Provenance

+ - { formData.verificationphotohash ? - - : null - } - - -
-

Provenance Information

-

{brand} {model}: {serial}

+ {formData.verificationphotohash ? ( + + ) : null} + + +
+

Provenance Information

+

+ {brand} {model}: {serial} +

- -
- + +
+
- + @@ -184,33 +206,21 @@ const TransferProfile = () => { - - - -
Provenance Contract Address + Provenance Contract Address + {provenanceContract.address}
Serial Number {provenanceProps.serial}
+ + - {errorMessage ? -

{errorMessage}

: null} - + {errorMessage ?

{errorMessage}

: null}
-
- - ) - - } else { - return( -

No pending transfer for this provenance.

- ) - } - -} - + + ); + } else { + return

No pending transfer for this provenance.

; + } +}; export default TransferProfile; - - /* these will only with an API endpoint calling a smart contract. nothing user based. @@ -251,8 +261,6 @@ export async function getStaticProps() { } */ - - /* original sale function function ClaimProvenance() { @@ -308,4 +316,4 @@ export async function getStaticProps() { ) } - */ \ No newline at end of file + */ diff --git a/pages/incoming-transfers/index.js b/pages/incoming-transfers/index.js index b8713a2..95ced31 100644 --- a/pages/incoming-transfers/index.js +++ b/pages/incoming-transfers/index.js @@ -1,144 +1,194 @@ -import { useEffect, useState } from 'react'; -import { ethers } from 'ethers'; +import { useEffect, useState } from "react"; +import { ethers } from "ethers"; //next imports -import Link from 'next/link' -import { useRouter } from 'next/router'; +import Link from "next/link"; +import { useRouter } from "next/router"; //react-bootstrap imports -import { Container, Carousel, Table, Row, Col, Card, Image, Button, ListGroup, ListGroupItem} from 'react-bootstrap'; +import { + Container, + Carousel, + Table, + Row, + Col, + Card, + Image, + Button, + ListGroup, + ListGroupItem, +} from "react-bootstrap"; // context imports -import { useItemContext } from '../../src/context/ItemContext'; -import { useContractContext } from '../../src/context/ContractContext'; -import { useUserContext } from '../../src/context/UserContext'; -import { useTransferContext } from '../../src/context/TransferContext'; +import { useItemContext } from "../../src/context/ItemContext"; +import { useContractContext } from "../../src/context/ContractContext"; +import { useUserContext } from "../../src/context/UserContext"; +import { useTransferContext } from "../../src/context/TransferContext"; //abi -import Provenance from '../../artifacts/contracts/Provenance.sol/Provenance.json'; +import Provenance from "../../artifacts/contracts/Provenance.sol/Provenance.json"; // styles -import styles from './transfers.module.css' - +import styles from "./transfers.module.css"; const IncomingTransfers = () => { - const { ipfsGetterRootURL } = useItemContext(); const { MothershipContract } = useContractContext(); const { mainAccount, provider, signer } = useUserContext(); - const { pendingTransferContracts, setPendingTransferContracts } = useTransferContext(); + const { pendingTransferContracts, setPendingTransferContracts } = + useTransferContext(); const router = useRouter(); + const [loaded, setLoaded] = useState(false); - const [ loaded , setLoaded ] = useState(false); - - const [buyerAccount, setBuyerAccount] = useState(''); - const [errorMessage, setErrorMessage] = useState(''); - + const [buyerAccount, setBuyerAccount] = useState(""); + const [errorMessage, setErrorMessage] = useState(""); const TransferTable = () => { - - - - if (pendingTransferContracts){ + if (pendingTransferContracts) { return ( <> -

this page is for incoming transfers only. Transferring provenances is only allowed from the individual item pages. Visit your items page to transfer provenances or here to learn how.

+

+ this page is for incoming transfers only. Transferring provenances + is only allowed from the individual item pages. Visit your{" "} + items page to transfer provenances + or here to learn how. +

- )} else { - return ( - null - ) - } -} - + ); + } else { + return null; + } + }; return ( <> - {pendingTransferContracts && pendingTransferContracts.length > 0 ? - - : -
-

You have no pending Provenance transfers.

-
- } + {pendingTransferContracts && pendingTransferContracts.length > 0 ? ( + + ) : ( +
+

+ You have no pending Provenance transfers. +

+
+ )} - - - ) - - -} - + ); +}; export default IncomingTransfers; - - - diff --git a/pages/incoming-transfers/transfers.module.css b/pages/incoming-transfers/transfers.module.css index 938be0e..fbdab50 100644 --- a/pages/incoming-transfers/transfers.module.css +++ b/pages/incoming-transfers/transfers.module.css @@ -1,19 +1,17 @@ .itemTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .tokenTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .container { - text-align: center; -} \ No newline at end of file + text-align: center; +} diff --git a/pages/index.js b/pages/index.js index 57cd0ae..fefd3e2 100644 --- a/pages/index.js +++ b/pages/index.js @@ -1,97 +1,106 @@ //next imports -import Head from 'next/head' -import Image from 'next/image' -import Link from 'next/link'; +import Head from "next/head"; +import Image from "next/image"; +import Link from "next/link"; -import Sample from './about/index.js' +import Sample from "./about/index.js"; -import { useState, useEffect, useRef} from 'react'; -import { ethers } from 'ethers'; +import { useState, useEffect, useRef } from "react"; +import { ethers } from "ethers"; // image imports -import fox from '../public/images/MetaMask_Fox.svg' -import mintNFT from '../public/images/mintNFT-graphic.png' -import tablet from '../public/images/tablet.jpeg' -import rosettaStone from '../public/images/799px-Rosetta_Stone_BW.jpeg' +import fox from "../public/images/MetaMask_Fox.svg"; +import mintNFT from "../public/images/mintNFT-graphic.png"; +import tablet from "../public/images/tablet.jpeg"; +import rosettaStone from "../public/images/799px-Rosetta_Stone_BW.jpeg"; -import arrow from '../public/images/iconmonstr-arrow-right-thin.svg' +import arrow from "../public/images/iconmonstr-arrow-right-thin.svg"; //style imports -import { Container, Row, Col, Navbar, Nav, Button } from 'react-bootstrap/' -import coverphoto from '../public/images/history-caveman-neanderthal-stone-carved_in_stone-everlasting-cza1482_low.jpeg' -import styles from './index.module.css' +import { Container, Row, Col, Navbar, Nav, Button } from "react-bootstrap/"; +import coverphoto from "../public/images/history-caveman-neanderthal-stone-carved_in_stone-everlasting-cza1482_low.jpeg"; +import styles from "./index.module.css"; // components imports // import { Earth } from '../src/components/Earth.js'; - //context imports -import { useUserContext } from '../src/context/UserContext.js'; -import { useContractContext} from '../src/context/ContractContext.js' - - - - +import { useUserContext } from "../src/context/UserContext.js"; +import { useContractContext } from "../src/context/ContractContext.js"; function App() { - - const {sharedState, mainAccount, setMainAccount, signer, provider, chainId} = useUserContext(); + const { + sharedState, + mainAccount, + setMainAccount, + signer, + provider, + chainId, + } = useUserContext(); const { MothershipContract, TokenContract } = useContractContext(); - - - const Home = () => { return ( - +
-

a blockchain-powered provenance and verification solution for
instruments and musical items

- {chainId && chainId != 1337 ?

PLEASE CONNECT TO LOCALHOST IN YOUR WALLET TO CONTINUE

: null} +

+ a blockchain-powered provenance and verification solution for
+ instruments and musical items +

+ {chainId && chainId != 31337 ? ( +

+ PLEASE CONNECT TO LOCALHOST IN YOUR WALLET TO CONTINUE +

+ ) : null} - - -
-

Know all this stuff and ready to get going?

- +
+

Know all this stuff and ready to get going?

+
); - } + }; const Visualization = () => { - return( + return (

Simple Two-Step Process to Creating a Provenance

-

Prerequisite: Create a Wallet (if you don't have one already)

+

+ Prerequisite: Create a Wallet (if + you don't have one already) +

{/* -

1. Mint an NFT with a verification photo

+

1. Mint an NFT with a verification photo

- +

- +
-

2. Add item information and additional photos to create the Provenance.

+

+ 2. Add item information and additional photos to create the + Provenance. +

- - + + />

Done!

-

for a step-by-step guide to creating a Provenance click here

-

for a more technical description of what is happening behind the scenes here

- - +

+ for a step-by-step guide to creating a Provenance click{" "} + here +

+

+ for a more technical description of what is happening behind the + scenes here +

+ + +
- - ) - } + ); + }; const HowDoesItWork = () => { - - return( + return (

HOW DOES IT WORK?

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Tempus egestas sed sed risus pretium quam. Maecenas volutpat blandit aliquam etiam. Quam elementum pulvinar etiam non quam lacus suspendisse. Rutrum quisque non tellus orci ac auctor augue mauris. Nunc faucibus a pellentesque sit. Amet massa vitae tortor condimentum lacinia quis vel. Mollis aliquam ut porttitor leo a. Ut eu sem integer vitae justo eget magna fermentum. Id diam vel quam elementum pulvinar etiam non quam lacus. Nunc consequat interdum varius sit amet mattis. Vitae aliquet nec ullamcorper sit amet. Urna neque viverra justo nec ultrices. Aenean pharetra magna ac placerat vestibulum lectus mauris. Tincidunt arcu non sodales neque sodales. Vulputate eu scelerisque felis imperdiet proin fermentum leo vel. Fermentum posuere urna nec tincidunt praesent. Odio ut sem nulla pharetra. Ipsum nunc aliquet bibendum enim facilisis gravida. Molestie a iaculis at erat pellentesque adipiscing commodo elit. - - Sed euismod nisi porta lorem. Neque vitae tempus quam pellentesque nec nam aliquam sem et. Tellus in metus vulputate eu scelerisque felis imperdiet. Vestibulum lorem sed risus ultricies tristique nulla. Orci ac auctor augue mauris. Non sodales neque sodales ut. Tincidunt tortor aliquam nulla facilisi cras fermentum odio eu. Vitae justo eget magna fermentum iaculis eu non diam. Mollis nunc sed id semper. Netus et malesuada fames ac turpis egestas maecenas pharetra. Lectus nulla at volutpat diam ut venenatis tellus in. Imperdiet sed euismod nisi porta lorem. Sed tempus urna et pharetra. Sit amet nisl purus in mollis nunc. Dui faucibus in ornare quam viverra orci sagittis. Etiam erat velit scelerisque in dictum non consectetur a. - - Ultrices mi tempus imperdiet nulla malesuada pellentesque elit eget. Vulputate odio ut enim blandit volutpat. Metus aliquam eleifend mi in. Feugiat in ante metus dictum at. Lorem ipsum dolor sit amet consectetur adipiscing elit pellentesque. Praesent tristique magna sit amet purus gravida quis blandit. Risus viverra adipiscing at in tellus. Fusce id velit ut tortor pretium viverra suspendisse potenti. Aliquet risus feugiat in ante metus dictum at. Nec ultrices dui sapien eget mi proin. Tortor vitae purus faucibus ornare suspendisse. Eros in cursus turpis massa tincidunt dui ut ornare. Consequat semper viverra nam libero justo laoreet sit. Egestas sed tempus urna et pharetra pharetra massa massa ultricies. Diam quam nulla porttitor massa id neque aliquam vestibulum morbi. - - Molestie ac feugiat sed lectus. Diam donec adipiscing tristique risus nec feugiat in fermentum posuere. Curabitur gravida arcu ac tortor dignissim convallis aenean et tortor. Et odio pellentesque diam volutpat commodo sed. Nunc aliquet bibendum enim facilisis gravida. Dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim. Tellus orci ac auctor augue. Eget nulla facilisi etiam dignissim diam quis enim lobortis. Sollicitudin aliquam ultrices sagittis orci a scelerisque purus semper eget. Elementum integer enim neque volutpat. Habitant morbi tristique senectus et. Convallis convallis tellus id interdum. Sit amet massa vitae tortor condimentum lacinia quis vel. Sit amet mauris commodo quis imperdiet massa. Massa massa ultricies mi quis hendrerit.

- +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Tempus + egestas sed sed risus pretium quam. Maecenas volutpat blandit aliquam + etiam. Quam elementum pulvinar etiam non quam lacus suspendisse. + Rutrum quisque non tellus orci ac auctor augue mauris. Nunc faucibus a + pellentesque sit. Amet massa vitae tortor condimentum lacinia quis + vel. Mollis aliquam ut porttitor leo a. Ut eu sem integer vitae justo + eget magna fermentum. Id diam vel quam elementum pulvinar etiam non + quam lacus. Nunc consequat interdum varius sit amet mattis. Vitae + aliquet nec ullamcorper sit amet. Urna neque viverra justo nec + ultrices. Aenean pharetra magna ac placerat vestibulum lectus mauris. + Tincidunt arcu non sodales neque sodales. Vulputate eu scelerisque + felis imperdiet proin fermentum leo vel. Fermentum posuere urna nec + tincidunt praesent. Odio ut sem nulla pharetra. Ipsum nunc aliquet + bibendum enim facilisis gravida. Molestie a iaculis at erat + pellentesque adipiscing commodo elit. Sed euismod nisi porta lorem. + Neque vitae tempus quam pellentesque nec nam aliquam sem et. Tellus in + metus vulputate eu scelerisque felis imperdiet. Vestibulum lorem sed + risus ultricies tristique nulla. Orci ac auctor augue mauris. Non + sodales neque sodales ut. Tincidunt tortor aliquam nulla facilisi cras + fermentum odio eu. Vitae justo eget magna fermentum iaculis eu non + diam. Mollis nunc sed id semper. Netus et malesuada fames ac turpis + egestas maecenas pharetra. Lectus nulla at volutpat diam ut venenatis + tellus in. Imperdiet sed euismod nisi porta lorem. Sed tempus urna et + pharetra. Sit amet nisl purus in mollis nunc. Dui faucibus in ornare + quam viverra orci sagittis. Etiam erat velit scelerisque in dictum non + consectetur a. Ultrices mi tempus imperdiet nulla malesuada + pellentesque elit eget. Vulputate odio ut enim blandit volutpat. Metus + aliquam eleifend mi in. Feugiat in ante metus dictum at. Lorem ipsum + dolor sit amet consectetur adipiscing elit pellentesque. Praesent + tristique magna sit amet purus gravida quis blandit. Risus viverra + adipiscing at in tellus. Fusce id velit ut tortor pretium viverra + suspendisse potenti. Aliquet risus feugiat in ante metus dictum at. + Nec ultrices dui sapien eget mi proin. Tortor vitae purus faucibus + ornare suspendisse. Eros in cursus turpis massa tincidunt dui ut + ornare. Consequat semper viverra nam libero justo laoreet sit. Egestas + sed tempus urna et pharetra pharetra massa massa ultricies. Diam quam + nulla porttitor massa id neque aliquam vestibulum morbi. Molestie ac + feugiat sed lectus. Diam donec adipiscing tristique risus nec feugiat + in fermentum posuere. Curabitur gravida arcu ac tortor dignissim + convallis aenean et tortor. Et odio pellentesque diam volutpat commodo + sed. Nunc aliquet bibendum enim facilisis gravida. Dictumst vestibulum + rhoncus est pellentesque elit ullamcorper dignissim. Tellus orci ac + auctor augue. Eget nulla facilisi etiam dignissim diam quis enim + lobortis. Sollicitudin aliquam ultrices sagittis orci a scelerisque + purus semper eget. Elementum integer enim neque volutpat. Habitant + morbi tristique senectus et. Convallis convallis tellus id interdum. + Sit amet massa vitae tortor condimentum lacinia quis vel. Sit amet + mauris commodo quis imperdiet massa. Massa massa ultricies mi quis + hendrerit. +

+ + +
- ) - } + ); + }; return ( - -
- - -
- - -
- - - - -
- - - - - +
+ +
+ +
+ + +
); } -export default App; \ No newline at end of file +export default App; diff --git a/pages/index.module.css b/pages/index.module.css index 6b3d3a7..6e05fc2 100644 --- a/pages/index.module.css +++ b/pages/index.module.css @@ -1,85 +1,79 @@ +.homeContainer { + height: 70vh; + margin-top: 30px; + padding-top: 15px; + border: 2px solid #1976d2; + border-radius: 25px; - .homeContainer { - height: 70vh; + /* to center the internal div for later use */ - margin-top: 30px; - padding-top: 15px; - border: 2px solid #1976d2; - border-radius: 25px; - - /* to center the internal div for later use */ - - /* display: flex; + /* display: flex; align-items: center; */ - - } - - .pageContainer{ - margin-top: 30px; - padding-top: 15px; - border: 2px solid #1976d2; - border-radius: 25px; - } - - .internalHomeContainer { - margin-top: 5%; - - } - - - - .introButton { - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 35px; - border-radius: 25px; - margin-top: 40px; - } - - .introButton:hover { - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 35px; - border-radius: 25px; - margin-top: 40px; - } - - .registerButton{ - display:inline-block; - padding:0.46em 1.6em; - margin:0 0.2em 0.2em 0; - box-sizing: border-box; - text-decoration:none; - color:#FFFFFF; - border-radius: 25px; - text-shadow: 0 0.04em 0.04em rgba(0,0,0,0.35); - background-color:green; - text-align:center; - transition: all 0.15s; - box-shadow: 10px 5px 20px rgb(162, 161, 161); - - font-weight: bolder; - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 300%; - } - - .flexContainerCentered { - display: flex; - justify-content: center; - align-items: center; - } - .registerButton:hover{ - text-shadow: 0 0 2em rgba(255,255,255,1); - color:#FFFFFF; - border-color:#FFFFFF; - font-weight: bolder; - border-radius: 25px; - } - - .graphicContainer { - position: relative; - height: 200px; - } - - .topButton { - margin-bottom: 10px; - } +} + +.pageContainer { + margin-top: 30px; + padding-top: 15px; + border: 2px solid #1976d2; + border-radius: 25px; +} + +.internalHomeContainer { + margin-top: 5%; +} + +.introButton { + font-family: -apple-system, BlinkMacSystemFont, sans-serif; + font-size: 35px; + border-radius: 25px; + margin-top: 40px; +} + +.introButton:hover { + font-family: -apple-system, BlinkMacSystemFont, sans-serif; + font-size: 35px; + border-radius: 25px; + margin-top: 40px; +} + +.registerButton { + display: inline-block; + padding: 0.46em 1.6em; + margin: 0 0.2em 0.2em 0; + box-sizing: border-box; + text-decoration: none; + color: #ffffff; + border-radius: 25px; + text-shadow: 0 0.04em 0.04em rgba(0, 0, 0, 0.35); + background-color: green; + text-align: center; + transition: all 0.15s; + box-shadow: 10px 5px 20px rgb(162, 161, 161); + + font-weight: bolder; + font-family: -apple-system, BlinkMacSystemFont, sans-serif; + font-size: 300%; +} + +.flexContainerCentered { + display: flex; + justify-content: center; + align-items: center; +} +.registerButton:hover { + text-shadow: 0 0 2em rgba(255, 255, 255, 1); + color: #ffffff; + border-color: #ffffff; + font-weight: bolder; + border-radius: 25px; +} + +.graphicContainer { + position: relative; + height: 200px; +} + +.topButton { + margin-bottom: 10px; +} diff --git a/pages/outgoing-transfers/index.js b/pages/outgoing-transfers/index.js index af0c347..f62d9e2 100644 --- a/pages/outgoing-transfers/index.js +++ b/pages/outgoing-transfers/index.js @@ -1,19 +1,10 @@ function OutgoingTransfersInfo() { - - - return ( - -
- - LOTS OF INFO ON HOW TO TRANSFERS PROVENANCES AND THEN BIG LINK TO ITEMS PAGE - -
- - - - - - ); - } - - export default OutgoingTransfersInfo; \ No newline at end of file + return ( +
+ LOTS OF INFO ON HOW TO TRANSFERS PROVENANCES AND THEN BIG LINK TO ITEMS + PAGE +
+ ); +} + +export default OutgoingTransfersInfo; diff --git a/pages/provenance-success/ProvenanceSuccess.js b/pages/provenance-success/ProvenanceSuccess.js index 4254131..bea9496 100644 --- a/pages/provenance-success/ProvenanceSuccess.js +++ b/pages/provenance-success/ProvenanceSuccess.js @@ -1,74 +1,86 @@ -import { ethers } from 'ethers'; -import { useState } from 'react'; - +import { ethers } from "ethers"; +import { useState } from "react"; // style -import styles from './ProvenanceSuccess.module.css'; +import styles from "./ProvenanceSuccess.module.css"; //next imports -import Link from 'next/link'; -import Image from 'next/image'; -import { useRouter } from 'next/router'; +import Link from "next/link"; +import Image from "next/image"; +import { useRouter } from "next/router"; //context imports -import { useUserContext } from '../../src/context/UserContext'; -import { useItemContext } from '../../src/context/ItemContext'; - -import { Button, Row, Col } from 'react-bootstrap'; +import { useUserContext } from "../../src/context/UserContext"; +import { useItemContext } from "../../src/context/ItemContext"; -import yes from '../../public/images/ndyes.gif'; -import greenCheckmark from '../../public/images/green_checkmark.png'; +import { Button, Row, Col } from "react-bootstrap"; +import yes from "../../public/images/ndyes.gif"; +import greenCheckmark from "../../public/images/green_checkmark.png"; const ProvenanceSuccess = () => { - - const { newProvenanceAddress } = useItemContext(); - const { mainAccount } = useUserContext(); - - // create conditional on new ProvenanceAddress so that it tells them to go to item page if there is not new provenance address - return( -
-
- - -

Provenance Successfully Created!

- checkmark - - -

{newProvenanceAddress ?

Your New Provenance is created at: {newProvenanceAddress}

: "newAddress not here"}

-

View this transaction on Etherscan: here

-

not that provenance contracts need to automatically verified on deployment so they are human readable

- - - - - - - - - - - - - {/*{mainAccount} Provenances*/} - - -
- {"loading..."} -
- -
+ const { newProvenanceAddress } = useItemContext(); + const { mainAccount } = useUserContext(); + + // create conditional on new ProvenanceAddress so that it tells them to go to item page if there is not new provenance address + return ( +
+
+

Provenance Successfully Created!

+ checkmark + +

+ {newProvenanceAddress ? ( +

Your New Provenance is created at: {newProvenanceAddress}

+ ) : ( + "newAddress not here" + )} +

+

+ View this transaction on Etherscan:{" "} + + {" "} + here{" "} + +

+

+ not that provenance contracts need to automatically verified on + deployment so they are human readable +

+ + + + + + + + + + + + + {/*{mainAccount} Provenances*/} + + +
+ {"loading..."}
- - ) -} +
+
+ ); +}; export default ProvenanceSuccess; - diff --git a/pages/provenance-success/ProvenanceSuccess.module.css b/pages/provenance-success/ProvenanceSuccess.module.css index 112a72e..8520fb6 100644 --- a/pages/provenance-success/ProvenanceSuccess.module.css +++ b/pages/provenance-success/ProvenanceSuccess.module.css @@ -1,26 +1,22 @@ /*currently not working*/ - .container { text-align: center; justify-content: center; } .provenanceSuccess { - border: 3px !important; - border-color: black !important; - } - - + border: 3px !important; + border-color: black !important; +} -.imageWrapper{ +.imageWrapper { position: relative; height: 300px; - } .CTAButton { font-size: 250%; border-radius: 25px; margin-bottom: 20px; -} \ No newline at end of file +} diff --git a/pages/provenance-success/index.js b/pages/provenance-success/index.js index aa1a5c1..6ad57c0 100644 --- a/pages/provenance-success/index.js +++ b/pages/provenance-success/index.js @@ -1,3 +1,3 @@ -import ProvenanceSuccess from './ProvenanceSuccess.js'; +import ProvenanceSuccess from "./ProvenanceSuccess.js"; -export default ProvenanceSuccess; \ No newline at end of file +export default ProvenanceSuccess; diff --git a/pages/provenances/ProvenanceHub.module.css b/pages/provenances/ProvenanceHub.module.css index 9ea6e28..a43ac0b 100644 --- a/pages/provenances/ProvenanceHub.module.css +++ b/pages/provenances/ProvenanceHub.module.css @@ -1,88 +1,78 @@ .itemTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .tokenTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .container { - text-align: center; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; - + text-align: center; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } .ownedContainer { - text-align: center; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; + text-align: center; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } .ownedContainer:hover { - background-color: azure; + background-color: azure; } .pendingContainer { - text-align: center; - border: 6px solid red; - border-radius: 25px; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; + text-align: center; + border: 6px solid red; + border-radius: 25px; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } .linkPlacebo { - color: #0d6efd; - text-decoration: underline; + color: #0d6efd; + text-decoration: underline; } .itemPhotoCarousel { - flex: 1; - object-fit: cover; - height: 300px; - + flex: 1; + object-fit: cover; + height: 300px; } - - - - .cardContainer { - display: flex; - height: 250px; + display: flex; + height: 250px; } .cardPhotoContainer { - flex: 1; - max-width: 25%; - object-fit: contain; + flex: 1; + max-width: 25%; + object-fit: contain; } .cardPhoto { - max-width: 100%; - max-height: 100%; - + max-width: 100%; + max-height: 100%; } .cardInfoContainer { - flex: 2; - border: solid 2px lightgray; - border-radius: 10px; + flex: 2; + border: solid 2px lightgray; + border-radius: 10px; - padding-top: 5px; - margin-left: 10px; - -} \ No newline at end of file + padding-top: 5px; + margin-left: 10px; +} diff --git a/pages/provenances/[provenance]/index.js b/pages/provenances/[provenance]/index.js index 1c3b9c1..d17eb22 100644 --- a/pages/provenances/[provenance]/index.js +++ b/pages/provenances/[provenance]/index.js @@ -1,35 +1,43 @@ -import { useEffect, useState } from 'react'; -import { ethers } from 'ethers'; +import { useEffect, useState } from "react"; +import { ethers } from "ethers"; //next imports -import Link from 'next/link' -import { useRouter } from 'next/router'; +import Link from "next/link"; +import { useRouter } from "next/router"; //react-bootstrap imports -import { Container, Carousel, Table, Row, Col, Card, Image, Button, ListGroup, ListGroupItem, InputGroup, Form, FormControl} from 'react-bootstrap'; +import { + Container, + Carousel, + Table, + Row, + Col, + Card, + Image, + Button, + ListGroup, + ListGroupItem, + InputGroup, + Form, + FormControl, +} from "react-bootstrap"; // context imports -import { useItemContext } from '../../../src/context/ItemContext'; -import { useContractContext } from '../../../src/context/ContractContext'; -import { useUserContext } from '../../../src/context/UserContext'; -import { useTransferContext } from '../../../src/context/TransferContext'; +import { useItemContext } from "../../../src/context/ItemContext"; +import { useContractContext } from "../../../src/context/ContractContext"; +import { useUserContext } from "../../../src/context/UserContext"; +import { useTransferContext } from "../../../src/context/TransferContext"; // components imports -import ProvenanceHistory from '../../../src/components/ProvenanceHistory/ProvenanceHistory.js' - +import ProvenanceHistory from "../../../src/components/ProvenanceHistory/ProvenanceHistory.js"; // styles -import styles from '../ProvenanceHub.module.css' - - - - - +import styles from "../ProvenanceHub.module.css"; const ProvenanceProfile = () => { - - const { provenanceObjects, ipfsGetterRootURL, bytes32ToString } = useItemContext(); + const { provenanceObjects, ipfsGetterRootURL, bytes32ToString } = + useItemContext(); const { TokenContract, MothershipContract } = useContractContext(); const { mainAccount, provider } = useUserContext(); const { setOutgoingContract } = useTransferContext(); @@ -37,176 +45,176 @@ const ProvenanceProfile = () => { const { provenance } = router.query; + const [loaded, setLoaded] = useState(false); - const [ loaded , setLoaded ] = useState(false); - - const [ provenanceContract, setProvenanceContract ] = useState(); - const [ provenanceProps, setProvenanceProps ] = useState(); - const [ provenanceOwnerInfo, setProvenanceOwnerInfo ] = useState(); + const [provenanceContract, setProvenanceContract] = useState(); + const [provenanceProps, setProvenanceProps] = useState(); + const [provenanceOwnerInfo, setProvenanceOwnerInfo] = useState(); - - - - //load all info - useEffect(async() => { + //load all info + useEffect(async () => { if (provenanceObjects && provenance) { - loadProvenance() - } + loadProvenance(); + } // would be faster search with an object not a for loop. not sure how that would work with the map though. - async function loadProvenance() { + async function loadProvenance() { for (let contract of provenanceObjects) { if (contract.ProvenanceContract.address == provenance) { - const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo } = contract; + const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo } = + contract; setProvenanceContract(ProvenanceContract); setProvenanceProps(ProvenanceProps); setProvenanceOwnerInfo(ProvenanceOwnerInfo); - setLoaded(true) + setLoaded(true); } } } - },[provenanceObjects, provenance]) - + }, [provenanceObjects, provenance]); function loadAndPush() { - const address = provenanceContract.address; - router.push(`/provenances/${address}/release-provenance`) - + const address = provenanceContract.address; + router.push(`/provenances/${address}/release-provenance`); } const ItemPhotoCarousel = () => { const itemPhotoArray = provenanceProps.itemPhotos; return ( -
+

Item Photos

- {itemPhotoArray.length > 0 ? - - {itemPhotoArray.map((photo, index)=> { - return ( - - item photos not yet loaded - - ) - })} - - - : + {itemPhotoArray.length > 0 ? ( + + {itemPhotoArray.map((photo, index) => { + return ( + + item photos not yet loaded + + ); + })} + + ) : (

Item Photos Loading

- +
- } - -
- ) - } - - - -// const ProvenanceHistory = () => { - -// const [ provenanceHistoryArray, setProvenanceHistoryArray ] = useState([]); - -// useEffect(async() => { - -// if (MothershipContract) { -// loadProvenanceHistory() -// } -// async function loadProvenanceHistory() { -// const history = await provenanceContract.getOwnershipHistory(); -// const historyShift = [...history]; -// historyShift.shift(); -// setProvenanceHistoryArray(historyShift); - -// } -// },[MothershipContract]) - - -// if (loaded){ -// return ( -//
-//
-//

Provenance Ownership History

-// {provenanceHistoryArray.map((provenanceOwnerInfo, index)=> { -// const { ownerAddress, date, verificationPhotoHash, ownerCount } = provenanceOwnerInfo; - -// const dateFormatted = bytes32ToString(date) - -// return ( - -// -// - -// Owner {ownerCount} -// -//
-//
-// {'verification -//
-//
-// Owner Address -// -// {ownerAddress} -// -// Date Acquired -// -// {dateFormatted} -// - -// Notes -// -// Additional data can maybe stored off-chain? -// -//
- - -//
- -//
-//
-//
-// ) -// }) -// } -//
- -// )} else { -// return ( -//

nothing to show here

-// )} -// } - - - - if (loaded) { - - const { serial, brand, instrumentDeedToken, model, year, typeOfProvenance } = provenanceProps; - - const brandFormatted = bytes32ToString(brand); - - const modelFormatted = bytes32ToString(model); - - const serialFormatted = bytes32ToString(serial); - + )} +
+ ); + }; + + // const ProvenanceHistory = () => { + + // const [ provenanceHistoryArray, setProvenanceHistoryArray ] = useState([]); + + // useEffect(async() => { + + // if (MothershipContract) { + // loadProvenanceHistory() + // } + // async function loadProvenanceHistory() { + // const history = await provenanceContract.getOwnershipHistory(); + // const historyShift = [...history]; + // historyShift.shift(); + // setProvenanceHistoryArray(historyShift); + + // } + // },[MothershipContract]) + + // if (loaded){ + // return ( + //
+ //
+ //

Provenance Ownership History

+ // {provenanceHistoryArray.map((provenanceOwnerInfo, index)=> { + // const { ownerAddress, date, verificationPhotoHash, ownerCount } = provenanceOwnerInfo; + + // const dateFormatted = bytes32ToString(date) + + // return ( + + // + // + + // Owner {ownerCount} + // + //
+ //
+ // {'verification + //
+ //
+ // Owner Address + // + // {ownerAddress} + // + // Date Acquired + // + // {dateFormatted} + // + + // Notes + // + // Additional data can maybe stored off-chain? + // + //
+ + //
+ + //
+ //
+ //
+ // ) + // }) + // } + //
+ + // )} else { + // return ( + //

nothing to show here

+ // )} + // } + + if (loaded) { + const { + serial, + brand, + instrumentDeedToken, + model, + year, + typeOfProvenance, + } = provenanceProps; + + const brandFormatted = bytes32ToString(brand); + + const modelFormatted = bytes32ToString(model); + + const serialFormatted = bytes32ToString(serial); return ( -
-

{brandFormatted} {modelFormatted}: {year}

+
+

+ {brandFormatted} {modelFormatted}: {year} +

Verification Photo Image

- -

Provenance Information

- + +

Provenance Information

+
- + @@ -237,29 +245,27 @@ const ProvenanceProfile = () => { - - - -
Provenance Contract Address + Provenance Contract Address + {provenanceContract.address}
Serial Number {serialFormatted}
- - - -
- - + + + + + +
+ +
- - - ) - - } else { - return( -

You do not own this Provenance.

- ) - } - -} - + + ); + } else { + return ( +

You do not own this Provenance.

+ ); + } +}; export default ProvenanceProfile; - diff --git a/pages/provenances/[provenance]/release-provenance.js b/pages/provenances/[provenance]/release-provenance.js index badd708..a4f9deb 100644 --- a/pages/provenances/[provenance]/release-provenance.js +++ b/pages/provenances/[provenance]/release-provenance.js @@ -1,238 +1,290 @@ -import { useEffect, useState } from 'react'; -import { ethers } from 'ethers'; +import { useEffect, useState } from "react"; +import { ethers } from "ethers"; //next imports -import Link from 'next/link' -import { useRouter } from 'next/router'; +import Link from "next/link"; +import { useRouter } from "next/router"; //react-bootstrap imports -import { Container, Carousel, Table, Row, Col, Card, Image, Button, ListGroup, ListGroupItem, InputGroup, Form, FormControl} from 'react-bootstrap'; +import { + Container, + Carousel, + Table, + Row, + Col, + Card, + Image, + Button, + ListGroup, + ListGroupItem, + InputGroup, + Form, + FormControl, +} from "react-bootstrap"; // context imports -import { useItemContext } from '../../../src/context/ItemContext'; -import { useContractContext } from '../../../src/context/ContractContext'; -import { useUserContext } from '../../../src/context/UserContext'; +import { useItemContext } from "../../../src/context/ItemContext"; +import { useContractContext } from "../../../src/context/ContractContext"; +import { useUserContext } from "../../../src/context/UserContext"; -import { useTransferContext } from '../../../src/context/TransferContext'; +import { useTransferContext } from "../../../src/context/TransferContext"; // styles -import styles from './release-provenance.module.css' -import { use } from 'chai'; +import styles from "./release-provenance.module.css"; +import { use } from "chai"; // images -import greenCheckMark from '../../../public/images/green_checkmark.png' - - +import greenCheckMark from "../../../public/images/green_checkmark.png"; const ReleaseProvenance = () => { + const { provenanceObjects, ipfsGetterRootURL } = useItemContext(); + const { TokenContract, MothershipContract } = useContractContext(); + const { mainAccount, provider } = useUserContext(); + const [pendingTransfer, setPendingTransfer] = useState(false); + const [transferInitiated, setTransferInitiated] = useState(false); + const [loaded, setLoaded] = useState(false); + const [pendingTransferAddress, setPendingTransferAddress] = useState(); + const [buyerAccount, setBuyerAccount] = useState(); + const [addressErrorMessage, setAddressErrorMessage] = useState(""); - const { provenanceObjects, ipfsGetterRootURL } = useItemContext(); - const { TokenContract, MothershipContract } = useContractContext(); - const { mainAccount, provider } = useUserContext(); - - const [ pendingTransfer, setPendingTransfer ] = useState(false); - const [ transferInitiated, setTransferInitiated ] = useState(false) - const [ loaded, setLoaded] = useState(false) - const [ pendingTransferAddress, setPendingTransferAddress ] = useState(); - const [ buyerAccount, setBuyerAccount ] = useState(); - const [ addressErrorMessage, setAddressErrorMessage ] = useState('') + const [successMessage, setSuccessMessage] = useState(""); - const [ successMessage, setSuccessMessage ] = useState(''); + const [tokenApproved, setTokenApproved] = useState(false); + const [approvedBuyerAccount, setApprovedBuyerAccount] = useState(); - const [ tokenApproved, setTokenApproved ] = useState(false); - const [ approvedBuyerAccount, setApprovedBuyerAccount ] = useState() + const [outgoingContract, setOutgoingContract] = useState(); + const [outgoingProvenanceProps, setOutgoingProvenanceProps] = useState(); + const [outgoingProvenanceOwnerInfo, setOutgoingProvenanceOwnerInfo] = + useState(); - const [ outgoingContract, setOutgoingContract ] = useState() - const [ outgoingProvenanceProps, setOutgoingProvenanceProps ] = useState() - const [ outgoingProvenanceOwnerInfo, setOutgoingProvenanceOwnerInfo ] = useState() + const [escrowActive, setEscowActive] = useState(); - const [ escrowActive, setEscowActive] = useState(); + const router = useRouter(); + const { provenance } = router.query; - const router = useRouter(); - const { provenance } = router.query; - - // this will change is prov objects is an object with address key instead of array - useEffect(async() => { - if (provenanceObjects && provenance) { - loadProvenance() - } - async function loadProvenance() { - for (let contract of provenanceObjects) { - if (contract.ProvenanceContract.address == provenance) { - const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo } = contract; - - setOutgoingContract(ProvenanceContract); - setOutgoingProvenanceProps(ProvenanceProps); - setOutgoingProvenanceOwnerInfo(ProvenanceOwnerInfo); - setLoaded(true) - } - } - } - },[provenanceObjects, provenance]) - - useEffect(async() => { - if (TokenContract && outgoingProvenanceProps) { - checkApproved(); + // this will change is prov objects is an object with address key instead of array + useEffect(async () => { + if (provenanceObjects && provenance) { + loadProvenance(); + } + async function loadProvenance() { + for (let contract of provenanceObjects) { + if (contract.ProvenanceContract.address == provenance) { + const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo } = + contract; + + setOutgoingContract(ProvenanceContract); + setOutgoingProvenanceProps(ProvenanceProps); + setOutgoingProvenanceOwnerInfo(ProvenanceOwnerInfo); + setLoaded(true); } - async function checkApproved() { - const result = await TokenContract.getApproved(outgoingProvenanceProps.instrumentDeedToken.toString()); - if (result === outgoingContract.address) { - setTokenApproved(true) - } - } - - },[TokenContract, outgoingProvenanceProps]) - - - - useEffect(async() => { - - if (outgoingContract || transferInitiated) { - const pendingOwner = await outgoingContract.pendingOwner() - - if (pendingOwner != ethers.constants.AddressZero) { - setPendingTransfer(true) - setPendingTransferAddress(pendingOwner) - }} else { - console.log("No Pending Owner") - } - - },[outgoingContract, transferInitiated]) - - - const handleChange = event => { - console.log(event.target.value, "value") - setBuyerAccount(event.target.value); + } } + }, [provenanceObjects, provenance]); - - async function approveTransfer() { - setSuccessMessage('') - setAddressErrorMessage('') - await TokenContract.approve(outgoingContract.address, outgoingProvenanceProps.instrumentDeedToken.toString()) - .then(async(result) => { - provider.waitForTransaction(result.hash) - .then(async(mined) => { - if (mined) { - setSuccessMessage('Transaction Success') - setTokenApproved(true) - }}) - .catch((error)=> { - console.log(error) - }) - }) - .catch((error)=> { - console.log(error.message, "User denied approval txn") - }) - } - - async function revokeTransferApproval() { - await TokenContract.approve(ethers.constants.AddressZero, outgoingProvenanceProps.instrumentDeedToken.toString()) - .then(async(result) => { - provider.waitForTransaction(result.hash) - .then(async(mined) => { - if (mined) { - setSuccessMessage('Transaction Success') - setTokenApproved(false) - }}) - .catch((error)=> { - console.log(error) - }) - }) - + useEffect(async () => { + if (TokenContract && outgoingProvenanceProps) { + checkApproved(); } - - // approve zero address for revoke approval for transfer - // break this into--> approve and then set pending owner - - async function release() { - setSuccessMessage('') - setAddressErrorMessage('') - setTransferInitiated(false) - if (ethers.utils.isAddress(buyerAccount) && buyerAccount != mainAccount) { - await outgoingContract.setPendingOwner(buyerAccount) - .then(async(result) => { - provider.waitForTransaction(result.hash) - .then(async(mined) => { - if (mined) { - setSuccessMessage('Transaction Success') - setTransferInitiated(true) - }} - )}) - .catch((error)=> { - console.log(error) - }) - } else { - setAddressErrorMessage('You are either attempting to transfer to your own Ethereum address or an invalid address. Please check and re-enter.') + async function checkApproved() { + const result = await TokenContract.getApproved( + outgoingProvenanceProps.instrumentDeedToken.toString() + ); + if (result === outgoingContract.address) { + setTokenApproved(true); } } - - if (loaded){ - return ( - -
-

Transfer Provenance

{outgoingContract.address}

-

include etherscan link maybe? also need it for approval txn and release txn

-
+ }, [TokenContract, outgoingProvenanceProps]); - {pendingTransfer ? - -
- {successMessage ? -

{successMessage}

: null} -

This provenance has been released and is awaiting claim & verification by:

{pendingTransferAddress}

-
- - : - -
- -

Step 1: Approve This Token For Transfer {tokenApproved ? : null}

-
- - - {tokenApproved ? : } - - -
- -

Step 2: Release this Provenance for Claim

-

This step ***cannot*** be undone. Please be sure and double-check everything.

-
- - {ethers.utils.isAddress(buyerAccount) ?

You are transferring this provenance to this address: {buyerAccount}

: null} - -
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
-
0x70997970C51812dc3A010C7d01b50e0d17dc79C8
- {addressErrorMessage ? -

{addressErrorMessage}

: null} -

Enter Eth Address to Transfer To:

- - {successMessage ? -

{successMessage}

: null} - - -
- -
- -
+ useEffect(async () => { + if (outgoingContract || transferInitiated) { + const pendingOwner = await outgoingContract.pendingOwner(); - } -
- )} else { - return ( -

Loading Provenance Information...

- ) + if (pendingOwner != ethers.constants.AddressZero) { + setPendingTransfer(true); + setPendingTransferAddress(pendingOwner); } + } else { + console.log("No Pending Owner"); } - - export default ReleaseProvenance; \ No newline at end of file + }, [outgoingContract, transferInitiated]); + + const handleChange = (event) => { + console.log(event.target.value, "value"); + setBuyerAccount(event.target.value); + }; + + async function approveTransfer() { + setSuccessMessage(""); + setAddressErrorMessage(""); + await TokenContract.approve( + outgoingContract.address, + outgoingProvenanceProps.instrumentDeedToken.toString() + ) + .then(async (result) => { + provider + .waitForTransaction(result.hash) + .then(async (mined) => { + if (mined) { + setSuccessMessage("Transaction Success"); + setTokenApproved(true); + } + }) + .catch((error) => { + console.log(error); + }); + }) + .catch((error) => { + console.log(error.message, "User denied approval txn"); + }); + } + + async function revokeTransferApproval() { + await TokenContract.approve( + ethers.constants.AddressZero, + outgoingProvenanceProps.instrumentDeedToken.toString() + ).then(async (result) => { + provider + .waitForTransaction(result.hash) + .then(async (mined) => { + if (mined) { + setSuccessMessage("Transaction Success"); + setTokenApproved(false); + } + }) + .catch((error) => { + console.log(error); + }); + }); + } + + // approve zero address for revoke approval for transfer + // break this into--> approve and then set pending owner + + async function release() { + setSuccessMessage(""); + setAddressErrorMessage(""); + setTransferInitiated(false); + if (ethers.utils.isAddress(buyerAccount) && buyerAccount != mainAccount) { + await outgoingContract + .setPendingOwner(buyerAccount) + .then(async (result) => { + provider.waitForTransaction(result.hash).then(async (mined) => { + if (mined) { + setSuccessMessage("Transaction Success"); + setTransferInitiated(true); + } + }); + }) + .catch((error) => { + console.log(error); + }); + } else { + setAddressErrorMessage( + "You are either attempting to transfer to your own Ethereum address or an invalid address. Please check and re-enter." + ); + } + } + + if (loaded) { + return ( + +
+

Transfer Provenance

+

{outgoingContract.address}

+

+ include etherscan link maybe? also need it for approval txn and + release txn +

+
+ + {pendingTransfer ? ( +
+ {successMessage ?

{successMessage}

: null} +

+ This provenance has been released and is awaiting claim & + verification by:

{pendingTransferAddress}

+

+
+ ) : ( +
+ +

+ Step 1: Approve This Token For Transfer{" "} + {tokenApproved ? ( + + ) : null} +

+
+ + + {tokenApproved ? ( + + ) : ( + + )} + + +
+ +

Step 2: Release this Provenance for Claim

+

+ This step ***cannot*** be undone. Please be sure and + double-check everything. +

+
+ + {ethers.utils.isAddress(buyerAccount) ? ( +

+ You are transferring this provenance to this address:{" "} + {buyerAccount} +

+ ) : null} + +
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266
+
0x70997970C51812dc3A010C7d01b50e0d17dc79C8
+ {addressErrorMessage ?

{addressErrorMessage}

: null} +

Enter Eth Address to Transfer To:

+ + {successMessage ?

{successMessage}

: null} + +
+ +
+
+ )} +
+ ); + } else { + return

Loading Provenance Information...

; + } +}; + +export default ReleaseProvenance; diff --git a/pages/provenances/[provenance]/release-provenance.module.css b/pages/provenances/[provenance]/release-provenance.module.css index a6a1515..da0db43 100644 --- a/pages/provenances/[provenance]/release-provenance.module.css +++ b/pages/provenances/[provenance]/release-provenance.module.css @@ -1,64 +1,59 @@ .transferContainer { - text-align: center; - border: 2px solid lightgray; - margin-top: 10px; - margin-bottom: 10px; - border-radius: 25px; - padding: 30px 30px; + text-align: center; + border: 2px solid lightgray; + margin-top: 10px; + margin-bottom: 10px; + border-radius: 25px; + padding: 30px 30px; } .itemTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .tokenTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .container { - text-align: center; + text-align: center; } -.containerBorder { - border: 3px solid red; - border-radius: 25px; - padding-top: 10px; - padding-bottom: 10px; - text-align: center; - +.containerBorder { + border: 3px solid red; + border-radius: 25px; + padding-top: 10px; + padding-bottom: 10px; + text-align: center; } .cardContainer { - display: flex; - height: 250px; + display: flex; + height: 250px; } .cardPhotoContainer { - flex: 1; - max-width: 25%; - object-fit: contain; + flex: 1; + max-width: 25%; + object-fit: contain; } .cardPhoto { - max-width: 100%; - max-height: 100%; - + max-width: 100%; + max-height: 100%; } .cardInfoContainer { - flex: 2; - border: solid 2px lightgray; - border-radius: 10px; + flex: 2; + border: solid 2px lightgray; + border-radius: 10px; - padding-top: 5px; - margin-left: 10px; - -} \ No newline at end of file + padding-top: 5px; + margin-left: 10px; +} diff --git a/pages/provenances/index.jsx b/pages/provenances/index.jsx index 397ba0e..e09f48a 100644 --- a/pages/provenances/index.jsx +++ b/pages/provenances/index.jsx @@ -1,186 +1,225 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState } from "react"; //ethers import -import { ethers } from 'ethers'; +import { ethers } from "ethers"; //nav imports -import RegisterItem from '../register-item/index.js' +import RegisterItem from "../register-item/index.js"; // import OwnedItem from '../owned-item/OwnedItem'; -import Link from 'next/link'; -import Image from 'next/image'; -import { useRouter } from 'next/router'; - +import Link from "next/link"; +import Image from "next/image"; +import { useRouter } from "next/router"; //context imports -import { useContractContext } from '../../src/context/ContractContext'; -import { useItemContext } from '../../src/context/ItemContext'; -import { useUserContext } from '../../src/context/UserContext'; +import { useContractContext } from "../../src/context/ContractContext"; +import { useItemContext } from "../../src/context/ItemContext"; +import { useUserContext } from "../../src/context/UserContext"; //style imports -import styles from './ProvenanceHub.module.css'; -import { Container, Table, Row, Col, Card, Spinner } from 'react-bootstrap'; -import { BeatLoader } from 'react-spinners'; - -import ItemTable from '../../src/components/PublicItemTable/PublicItemTable.js'; - +import styles from "./ProvenanceHub.module.css"; +import { Container, Table, Row, Col, Card, Spinner } from "react-bootstrap"; +import { BeatLoader } from "react-spinners"; +import ItemTable from "../../src/components/PublicItemTable/PublicItemTable.js"; const OwnedProvenanceHub = () => { - - - const { mainAccount, signer } = useUserContext() - const {itemAdded,setItemAdded, provenanceObjects, ipfsGetterRootURL, bytes32ToString} = useItemContext(); + const { mainAccount, signer } = useUserContext(); + const { + itemAdded, + setItemAdded, + provenanceObjects, + ipfsGetterRootURL, + bytes32ToString, + } = useItemContext(); const [loading, setLoading] = useState(false); const [hasProvenances, setHasProvenances] = useState(true); const router = useRouter(); // to rebuild page and add new item. can probably be optimized. - useEffect(async() => { + useEffect(async () => { const confirmAddition = await itemAdded; - if (confirmAddition){ + if (confirmAddition) { router.reload(); - setItemAdded(false) - } - },[]) + setItemAdded(false); + } + }, []); - useEffect(async() => { + useEffect(async () => { setLoading(true); - setHasProvenances(false) + setHasProvenances(false); const result = await provenanceObjects; if (result.length > 0) { - setHasProvenances(true) - }; - - },[provenanceObjects]) - - useEffect(async() => { - setLoading(false) - - },[hasProvenances]) + setHasProvenances(true); + } + }, [provenanceObjects]); + useEffect(async () => { + setLoading(false); + }, [hasProvenances]); const OwnedProvenanceTable = () => { - - if (provenanceObjects && provenanceObjects.length > 0 && !loading){ + if (provenanceObjects && provenanceObjects.length > 0 && !loading) { return ( <> - {provenanceObjects.map((array, index) => { - - const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo, ProvenancePendingOwner } = array; - const provenanceAddress = ProvenanceContract.address; - const{ serial, brand, instrumentDeedToken, model, year, typeOfProvenance } = ProvenanceProps; - const { ownerAddress, name, verificationPhotoHash, date} = ProvenanceOwnerInfo; - - const pendingBool = !(ProvenancePendingOwner === ethers.constants.AddressZero); - const ownerBool = (ownerAddress === mainAccount); - - const serialFormatted = bytes32ToString(serial); - const brandFormatted = bytes32ToString(brand); - const modelFormatted = bytes32ToString(model); - - - return ( + const { + ProvenanceContract, + ProvenanceProps, + ProvenanceOwnerInfo, + ProvenancePendingOwner, + } = array; + const provenanceAddress = ProvenanceContract.address; + const { + serial, + brand, + instrumentDeedToken, + model, + year, + typeOfProvenance, + } = ProvenanceProps; + const { ownerAddress, name, verificationPhotoHash, date } = + ProvenanceOwnerInfo; + + const pendingBool = !( + ProvenancePendingOwner === ethers.constants.AddressZero + ); + const ownerBool = ownerAddress === mainAccount; + + const serialFormatted = bytes32ToString(serial); + const brandFormatted = bytes32ToString(brand); + const modelFormatted = bytes32ToString(model); + return (
- - - - - -

{brandFormatted} {modelFormatted}

- - {pendingBool ?

This provenance has been released and is awaiting claim by buyer {ProvenancePendingOwner}

: null } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Provenance Address: {provenanceAddress}
Brand: {brandFormatted}
Model: {modelFormatted}
Token ID: {instrumentDeedToken.toString()}
Type of Item: {typeOfProvenance.toString()}
Year: {year}
- - -
-
-
- + + +

+ {brandFormatted} {modelFormatted} +

+ + {pendingBool ? ( +

+ This provenance has been released and is awaiting + claim by buyer {ProvenancePendingOwner} +

+ ) : null} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Provenance Address: {provenanceAddress} +
Brand: {brandFormatted}
Model: {modelFormatted}
+ Token ID: {instrumentDeedToken.toString()} +
+ Type of Item: {typeOfProvenance.toString()} +
Year: {year}
+ +
+
+
-
+
- ) - }) - } - - + ); + })} - )} else { - return ( - <> - {loading ? -
-

Loading Your Provenances...

- -
- : - !hasProvenances ? null : -

You have no registered Provenances.

- } - - - - )} -} - - - - return( -
-

Registered Provenances for
{mainAccount}

- {/* + ); + } else { + return ( + <> + {loading ? ( +
+

Loading Your Provenances...

+ +
+ ) : !hasProvenances ? null : ( +

+ You have no registered Provenances. +

+ )} + + ); + } + }; + + return ( +
+

+ Registered Provenances for
{mainAccount} +

+ {/* */} - {/**/} - - - -
- ) -} - + {/**/} + +
+ ); +}; export default OwnedProvenanceHub; - - - diff --git a/pages/register-item/RegisterItem.jsx b/pages/register-item/RegisterItem.jsx index e1aadab..d8dda3e 100644 --- a/pages/register-item/RegisterItem.jsx +++ b/pages/register-item/RegisterItem.jsx @@ -1,550 +1,653 @@ -import { useState, useEffect, useReducer } from 'react'; +import { useState, useEffect, useReducer } from "react"; //import { Routes, Route, Link, useNavigate } from "react-router-dom"; -import { ethers } from 'ethers'; +import { ethers } from "ethers"; //next imports -import Link from 'next/link' -import { useRouter } from 'next/router'; +import Link from "next/link"; +import { useRouter } from "next/router"; //component imports -import DragAndDrop from '../../src/components/DragAndDrop.js'; -import PhotoPreviews from '../../src/components/PhotoPreviews.js'; +import DragAndDrop from "../../src/components/DragAndDrop.js"; +import PhotoPreviews from "../../src/components/PhotoPreviews.js"; // import { Modal } from '../../src/components/Modal.js'; -import { ConfirmationModal } from '../../src/components/ConfirmationModal.js'; +import { ConfirmationModal } from "../../src/components/ConfirmationModal.js"; //context imports -import { useUserContext } from '../../src/context/UserContext.js'; -import { useItemContext } from '../../src/context/ItemContext.js'; -import { useContractContext } from '../../src/context/ContractContext.js'; +import { useUserContext } from "../../src/context/UserContext.js"; +import { useItemContext } from "../../src/context/ItemContext.js"; +import { useContractContext } from "../../src/context/ContractContext.js"; //page imports -import ProvenanceSuccess from '../provenance-success/ProvenanceSuccess.js'; +import ProvenanceSuccess from "../provenance-success/ProvenanceSuccess.js"; //styling imports -import styles from "./RegisterItem.module.css" -import { Form, InputGroup, Container, Button, Row, Col, Dropdown, Accordion } from 'react-bootstrap/' - - +import styles from "./RegisterItem.module.css"; +import { + Form, + InputGroup, + Container, + Button, + Row, + Col, + Dropdown, + Accordion, +} from "react-bootstrap/"; const formReducer = (state, event) => { - if(event.reset) { + if (event.reset) { return { - type: '', - serial: '', - brand: '', + type: "", + serial: "", + brand: "", year: 0, token: 0, date: 0, verificationphotohash: [], instrumentphotohashes: [], - } + }; } - + return { ...state, [event.name]: event.value, - } - - } + }; +}; const RegisterItem = () => { + const [formData, setFormData] = useReducer(formReducer, { + instrumentphotohashes: [], + verificationphotohash: [], + }); + const [modalShow, setModalShow] = useState(false); //bootstrapmodal + // tokens + const [tokenId, setTokenId] = useState([]); + const [tokenToMint, setTokenToMint] = useState(0); + const [unusedTokenID, setUnusedTokenID] = useState(0); + // form + const [readyToMint, setReadyToMint] = useState(false); + const [enableForm, setEnableForm] = useState(false); + const [validated, setValidated] = useState(false); + const [submitting, setSubmitting] = useState(false); + + const [itemPhotosUploaded, setItemPhotosUploaded] = useState(false); + + const [mintErrorMessage, setMintErrorMessage] = useState(""); + const [mintSuccessMessage, setMintSuccessMessage] = useState(""); + + //context + const { mainAccount, provider, signer, dateString } = useUserContext(); + const { MothershipContract, TokenContract } = useContractContext(); + const { stringToBytes32 } = useItemContext(); + + const { + items, + setItems, + setItemAdded, + tokens, + setTokens, + provenanceObjects, + setProvenanceObjects, + newProvenanceAddress, + setNewProvenanceAddress, + ipfsGetterRootURL, + } = useItemContext(); + + const router = useRouter(); + + // low priority but keep this lifted, and fixed undefined on child components + //const [ipfsGetterRootURL] = useState("https://gateway.pinata.cloud/ipfs/"); + + //let navigate = useNavigate(); + + useEffect(() => { + if (TokenContract) { + TokenToMint(); + } + async function TokenToMint() { + TokenContract.totalSupply().then((result) => + setTokenToMint(result.toNumber()) + ); + } + }, [TokenContract, tokens]); - const [formData, setFormData] = useReducer(formReducer, {instrumentphotohashes : [], verificationphotohash : []}); - const [modalShow, setModalShow] = useState(false) //bootstrapmodal - // tokens - const [tokenId, setTokenId] = useState([]); - const [tokenToMint, setTokenToMint] = useState(0); - const [unusedTokenID, setUnusedTokenID] = useState(0); - // form - const [readyToMint, setReadyToMint] = useState(false); - const [enableForm, setEnableForm] = useState(false); - const [validated, setValidated] = useState(false); - const [submitting, setSubmitting] = useState(false); - - const [itemPhotosUploaded, setItemPhotosUploaded ] = useState(false); - - const [mintErrorMessage, setMintErrorMessage] = useState(''); - const [mintSuccessMessage, setMintSuccessMessage] = useState('') - - - - //context - const { mainAccount, provider, signer, dateString } = useUserContext(); - const { MothershipContract, TokenContract } = useContractContext(); - const { stringToBytes32 } = useItemContext(); - - - const {items, - setItems, - setItemAdded, - tokens, - setTokens, - provenanceObjects, - setProvenanceObjects, - newProvenanceAddress, - setNewProvenanceAddress, - ipfsGetterRootURL } = useItemContext(); - - - const router = useRouter(); - - // low priority but keep this lifted, and fixed undefined on child components - //const [ipfsGetterRootURL] = useState("https://gateway.pinata.cloud/ipfs/"); - - //let navigate = useNavigate(); - - useEffect(() => { - if (TokenContract) { - TokenToMint(); - } - - async function TokenToMint() { - TokenContract.totalSupply() - .then(result => (setTokenToMint(result.toNumber()))) - } - - },[TokenContract, tokens]) - - useEffect(() => { - if (readyToMint === false && formData.verificationphotohash.length > 0) { - setEnableForm(true) - } else { - setEnableForm(false) - } - - - },[readyToMint, formData.verificationphotohash]) - - - const handleSubmit = (event) => { - const form = event.currentTarget; - console.log(form.checkValidity(), "checking Validity") - if (form.checkValidity() === false) { - event.preventDefault(); - event.stopPropagation(); - setValidated(true) - } else { - event.preventDefault(); - setModalShow(true) - setSubmitting(true); - } - }; + useEffect(() => { + if (readyToMint === false && formData.verificationphotohash.length > 0) { + setEnableForm(true); + } else { + setEnableForm(false); + } + }, [readyToMint, formData.verificationphotohash]); + + const handleSubmit = (event) => { + const form = event.currentTarget; + console.log(form.checkValidity(), "checking Validity"); + if (form.checkValidity() === false) { + event.preventDefault(); + event.stopPropagation(); + setValidated(true); + } else { + event.preventDefault(); + setModalShow(true); + setSubmitting(true); + } + }; + const handleChange = (event) => { + const isCheckbox = event.target.type === "checkbox"; + setFormData({ + name: event.target.name, + value: isCheckbox ? event.target.checked : event.target.value, + }); + }; - const handleChange = event => { - const isCheckbox = event.target.type === 'checkbox'; - setFormData({ - name: event.target.name, - value: isCheckbox ? event.target.checked : event.target.value, - }) - } - - // functions + // functions const createProvenance = () => { MothershipContract.createNewProvenance( - formData.type, - stringToBytes32((formData.serial).toString()), - stringToBytes32(formData.brand) , - stringToBytes32(formData.model), - formData.year, - unusedTokenID, + formData.type, + stringToBytes32(formData.serial.toString()), + stringToBytes32(formData.brand), + stringToBytes32(formData.model), + formData.year, + unusedTokenID, stringToBytes32(dateString), - formData.verificationphotohash, - formData.instrumentphotohashes) - .then(async(result) => { - provider.waitForTransaction(result.hash) - .then(mined => { - if (mined) { - MothershipContract.once("ProvenanceCreated", (type, newAddress) => { - setNewProvenanceAddress(newAddress) - setItemAdded(true) - router.push('/provenance-success') - })} - }) - }) - } - - + formData.verificationphotohash, + formData.instrumentphotohashes + ).then(async (result) => { + provider.waitForTransaction(result.hash).then((mined) => { + if (mined) { + MothershipContract.once("ProvenanceCreated", (type, newAddress) => { + setNewProvenanceAddress(newAddress); + setItemAdded(true); + router.push("/provenance-success"); + }); + } + }); + }); + }; //TokenContract Interaction Functions - const mintToken = async() => { + const mintToken = async () => { if (!readyToMint) { - setMintErrorMessage("Upload a verfication photo to mint token") - console.log(readyToMint) + setMintErrorMessage("Upload a verfication photo to mint token"); + console.log(readyToMint); } else { - //mint token, need to include image URI - setMintSuccessMessage('') - await TokenContract.safeMint(mainAccount, formData.verificationphotohash) - .then(async(result) => { - provider.waitForTransaction(result.hash) - .then(mined => { - if (mined) { - TokenContract.once("TokenCreated" , async(owner, tokenId, success) => { - if (success) { - setTokens([...tokens, tokenId.toNumber()]) - setUnusedTokenID(tokenId.toNumber()) - setMintSuccessMessage('Token Successfully Minted!') - } - setFormData({ - name: 'verificationphotohash', - value: formData.verificationphotohash - }) - setReadyToMint(false) - })} - }) + //mint token, need to include image URI + setMintSuccessMessage(""); + await TokenContract.safeMint(mainAccount, formData.verificationphotohash) + .then(async (result) => { + provider.waitForTransaction(result.hash).then((mined) => { + if (mined) { + TokenContract.once( + "TokenCreated", + async (owner, tokenId, success) => { + if (success) { + setTokens([...tokens, tokenId.toNumber()]); + setUnusedTokenID(tokenId.toNumber()); + setMintSuccessMessage("Token Successfully Minted!"); + } + setFormData({ + name: "verificationphotohash", + value: formData.verificationphotohash, + }); + setReadyToMint(false); + } + ); + } + }); }) - .then(result => {console.log(result, "result")}) - .catch((error) => console.log(error.data)) - } + .then((result) => { + console.log(result, "result"); + }) + .catch((error) => console.log(error.data)); } + }; //Page Components //check if token has been used in a Provenance - const UserTokens= () => { - let provenanceTokens = []; - let unusedTokens = []; + const UserTokens = () => { + let provenanceTokens = []; + let unusedTokens = []; - useEffect(async() => { - if (provenanceObjects && provenanceObjects.length > 0){ + useEffect(async () => { + if (provenanceObjects && provenanceObjects.length > 0) { for (let i = 0; i < provenanceObjects.length; i++) { - provenanceTokens.push(provenanceObjects[i].ProvenanceProps.instrumentDeedToken) + provenanceTokens.push( + provenanceObjects[i].ProvenanceProps.instrumentDeedToken + ); } } - }, [provenanceObjects]) - - - tokens.forEach((token) => { - const result = provenanceTokens.includes(token); - if (!result) { - unusedTokens.push(token) - } - }) - - async function getTokenProps(unusedToken) { - const uri = await TokenContract.tokenURI(unusedToken); - setFormData({ - name: 'verificationphotohash', - value: uri}) - setUnusedTokenID(unusedToken) - setMintErrorMessage('') - setReadyToMint(false) + }, [provenanceObjects]); + + tokens.forEach((token) => { + const result = provenanceTokens.includes(token); + if (!result) { + unusedTokens.push(token); } + }); - //helper functions + async function getTokenProps(unusedToken) { + const uri = await TokenContract.tokenURI(unusedToken); + setFormData({ + name: "verificationphotohash", + value: uri, + }); + setUnusedTokenID(unusedToken); + setMintErrorMessage(""); + setReadyToMint(false); + } - function resetTokenDetails() { - {/*setTokenToMint(tokens.slice(-1))*/} - setFormData({ - name: "verificationphotohash", - value: [], - }) - setReadyToMint(false) - setItemPhotosUploaded(false) - } + //helper functions + + function resetTokenDetails() { + { + /*setTokenToMint(tokens.slice(-1))*/ + } + setFormData({ + name: "verificationphotohash", + value: [], + }); + setReadyToMint(false); + setItemPhotosUploaded(false); + } return ( <> - +
- +
- - {formData.verificationphotohash.length > 0 && readyToMint ? - - - - : - null } + + {formData.verificationphotohash.length > 0 && readyToMint ? ( - - - Tokens Awaiting Provenance - - - {unusedTokens.length > 0 ? + + + ) : null} + + + + Tokens Awaiting Provenance + + + {unusedTokens.length > 0 ? ( -
+
{unusedTokens.map((unusedToken) => { - return getTokenProps(unusedToken)}>{unusedToken} - })} -
- - -: + return ( + getTokenProps(unusedToken)} + > + {unusedToken} + + ); + })} +
+
+ ) : ( - there are no unused tokens + + there are no unused tokens + - } -
- {provenanceTokens.length > 0 ? - - - - Tokens already attached to Provenance - - {provenanceTokens} - - - - - : - null - } - + )} +
+ {provenanceTokens.length > 0 ? ( + + + + + Tokens already attached to Provenance + + {provenanceTokens} + + + + ) : null} +
- - ) - } - - - - return( -
- -

Create A Deed of Provenance

-

First time minting? Follow the tutorial

-

STEP 1: MINT TOKEN (NFT)

- - - What happens in Step 1? - - Creation of a Provenance is a two part process. The NFT (also referred to as the token) acts as the proof of ownership for the Provenance itself. - The behind-the-scenes mechanism to ensure your ownership of this token is industry-standard and audited by third party auditing experts, ensuring - security. -
-
- This token also requires a verification photo prove your ownership of the item. This photo needs to include your face and a picture of the item, preferably with serial number visible. -
-
-
- {/* Verfication Photo Upload */} - {readyToMint === false && formData.verificationphotohash.length > 0 ? - - null - : - - - - } - {/* Verication Photo Preview */} - { formData.verificationphotohash ? -
- - {mintSuccessMessage ?

Mint Success!

: null} -
- : null} - - {/* Token Updates */} - - + ); + }; + + return ( +
+ +

Create A Deed of Provenance

+

+ First time minting? Follow the{" "} + tutorial +

+

STEP 1: MINT TOKEN (NFT)

+ + + + What happens in Step 1? + + + Creation of a Provenance is a two part process. The NFT (also + referred to as the token) acts as the proof of ownership for the + Provenance itself. The behind-the-scenes mechanism to ensure your + ownership of this token is industry-standard and audited by third + party auditing experts, ensuring security. +
+
+ This token also requires a verification photo prove your ownership + of the item. This photo needs to include your face and a picture + of the item, preferably with serial number visible. +
+
+
+ {/* Verfication Photo Upload */} + {readyToMint === false && + formData.verificationphotohash.length > 0 ? null : ( + + + )} + {/* Verication Photo Preview */} + {formData.verificationphotohash ? ( +
+ + {mintSuccessMessage ? ( +

Mint Success!

+ ) : null} +
+ ) : null} + + {/* Token Updates */} + + + -

STEP 2: CREATE PROVENANCE WITH MINTED TOKEN

- - - - What happens in Step 2? - - In this step you enter all the identifying information about your item. All of this info will be stored on a blockchain in it's own unique smart contract forever and is publicy verifiable at any time (the link to view - the contract on-chain is in the individual item page). -
-
- You also have the option upload additional photos of the item (20 max). -
-
-
- - {/* Form */} - {/* Eventually Form and User Tokens need to be broken into separate components */} - -
-
- {enableForm ? -

Create Provenance with token # {unusedTokenID}

- : -

Mint a New Token or Select an Unused Token to Create a Provenance

- } - - {enableForm ? -

Step 1: Input Item Details

- : - null - } - - - + STEP 2: CREATE PROVENANCE WITH MINTED TOKEN + + + + + + What happens in Step 2? + + + In this step you enter all the identifying information about your + item. All of this info will be stored on a blockchain in it's own + unique smart contract forever and is publicy verifiable at any + time (the link to view the contract on-chain is in the individual + item page). +
+
+ You also have the option upload additional photos of the item (20 + max). +
+
+
+ + {/* Form */} + {/* Eventually Form and User Tokens need to be broken into separate components */} + + +
+ {enableForm ? ( +

Create Provenance with token # {unusedTokenID}

+ ) : ( +

+ {" "} + Mint a New Token or Select an Unused Token to Create a + Provenance{" "} +

+ )} + + {enableForm ? ( +

+ {" "} + Step 1: Input Item Details{" "} +

+ ) : null} + + + Input Type of Item + + + + + + + + Please select an item type. + + + + + + Item Serial Number + + + + + Please enter a serial number for your item. + + + + + Year of Manufacture + + + + Please enter a year of manufacture for your item. + + + + + + + + Brand + - Input Type of Item - - - - - - - - Please select an item type. - - - - - - - Item Serial Number - - - - - Please enter a serial number for your item. - - - - - Year of Manufacture - - - - Please enter a year of manufacture for your item. - - - - - - - - Brand - - - - - - - - - - - - - - - - - - Select Model of Item - - - - - - - - - - - - - - - - - {enableForm ? - -

Upload Additional Images

- -
: null } - - { formData.instrumentphotohashes.length >= 1 ? - - : null - } -
-
-
- -
-
- - - - - - {/*bootstrap modal*/} - { - setModalShow(false) - setSubmitting(false) - }} - formdata={formData} - createprovenance={createProvenance} - tokenid={tokenId} - unusedtokenid={unusedTokenID} - setSubmitting={setSubmitting} - /> - - -
- - ) -} + isInvalid="" + name="brand" + onChange={handleChange} + value={formData.brand || ""} + > + + + + + + + + + + + + +
+ + + + Select Model of Item + + + + + + + + + + + + + + + + {enableForm ? ( + +

Upload Additional Images

+ +
+ ) : null} + + {formData.instrumentphotohashes.length >= 1 ? ( + + ) : null} +
+
+
+ +
+ + +
+ + {/*bootstrap modal*/} + { + setModalShow(false); + setSubmitting(false); + }} + formdata={formData} + createprovenance={createProvenance} + tokenid={tokenId} + unusedtokenid={unusedTokenID} + setSubmitting={setSubmitting} + /> +
+ ); +}; export default RegisterItem; - - - diff --git a/pages/register-item/RegisterItem.module.css b/pages/register-item/RegisterItem.module.css index 0232cd6..49b9d07 100644 --- a/pages/register-item/RegisterItem.module.css +++ b/pages/register-item/RegisterItem.module.css @@ -1,13 +1,13 @@ /* New Provenance */ .NewProvenance { - display: block; - text-align: center; + display: block; + text-align: center; } .NewProvenance fieldset { - margin: 20px 0; - text-align: center; + margin: 20px 0; + text-align: center; } /* Text */ @@ -17,10 +17,10 @@ } .previewContainer { - display: flex; - min-height: 200px; - width: 80%; - margin: 0 auto; + display: flex; + min-height: 200px; + width: 80%; + margin: 0 auto; } .buttonContainer { @@ -33,7 +33,6 @@ .resetButton { font-size: 10px; margin-bottom: 20px; - } .submitButton { @@ -42,58 +41,59 @@ font-size: 40px; padding: 10px 30px; margin-bottom: 30px; - transition: background-color .5s ease-out 100ms; - + transition: background-color 0.5s ease-out 100ms; } .submitButton:hover { background-color: red; - } .previewPhoto { - flex: 1; - max-width: 50%; + flex: 1; + max-width: 50%; } .image { - width: 80%; - height: 80%; - object-fit: cover; + width: 80%; + height: 80%; + object-fit: cover; } /* Success Message */ -.mintSuccess{ +.mintSuccess { position: absolute; width: 100vw; top: 40%; left: 50%; font-size: 700%; color: green; - transform: translate(-50%,-50%); - -ms-transform: translate(-50%,-50%); + transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); - animation:mint-success 1s 1; - -webkit-animation:mint-success 0.5s 1; + animation: mint-success 1s 1; + -webkit-animation: mint-success 0.5s 1; animation-fill-mode: forwards; - animation-delay:3s; - -webkit-animation-delay:4s; /* Safari and Chrome + animation-delay: 3s; + -webkit-animation-delay: 4s; /* Safari and Chrome -webkit-animation-fill-mode: forwards; */ - -} - -@keyframes mint-success{ - from {opacity :1;} - to {opacity :0;} } -@-webkit-keyframes mint-success{ - from {opacity :1;} - to {opacity :0;} +@keyframes mint-success { + from { + opacity: 1; + } + to { + opacity: 0; + } } - - - +@-webkit-keyframes mint-success { + from { + opacity: 1; + } + to { + opacity: 0; + } +} diff --git a/pages/register-item/index.js b/pages/register-item/index.js index 83232ef..4dccca7 100644 --- a/pages/register-item/index.js +++ b/pages/register-item/index.js @@ -1,3 +1,3 @@ -import RegisterItem from './RegisterItem.jsx'; +import RegisterItem from "./RegisterItem.jsx"; -export default RegisterItem; \ No newline at end of file +export default RegisterItem; diff --git a/pages/search/Search.module.css b/pages/search/Search.module.css index 9ea6e28..a43ac0b 100644 --- a/pages/search/Search.module.css +++ b/pages/search/Search.module.css @@ -1,88 +1,78 @@ .itemTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .tokenTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .container { - text-align: center; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; - + text-align: center; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } .ownedContainer { - text-align: center; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; + text-align: center; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } .ownedContainer:hover { - background-color: azure; + background-color: azure; } .pendingContainer { - text-align: center; - border: 6px solid red; - border-radius: 25px; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; + text-align: center; + border: 6px solid red; + border-radius: 25px; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } .linkPlacebo { - color: #0d6efd; - text-decoration: underline; + color: #0d6efd; + text-decoration: underline; } .itemPhotoCarousel { - flex: 1; - object-fit: cover; - height: 300px; - + flex: 1; + object-fit: cover; + height: 300px; } - - - - .cardContainer { - display: flex; - height: 250px; + display: flex; + height: 250px; } .cardPhotoContainer { - flex: 1; - max-width: 25%; - object-fit: contain; + flex: 1; + max-width: 25%; + object-fit: contain; } .cardPhoto { - max-width: 100%; - max-height: 100%; - + max-width: 100%; + max-height: 100%; } .cardInfoContainer { - flex: 2; - border: solid 2px lightgray; - border-radius: 10px; + flex: 2; + border: solid 2px lightgray; + border-radius: 10px; - padding-top: 5px; - margin-left: 10px; - -} \ No newline at end of file + padding-top: 5px; + margin-left: 10px; +} diff --git a/pages/search/[nonUserProvenance]/index.js b/pages/search/[nonUserProvenance]/index.js index 7407e81..076b70c 100644 --- a/pages/search/[nonUserProvenance]/index.js +++ b/pages/search/[nonUserProvenance]/index.js @@ -1,34 +1,38 @@ -import { useEffect, useState } from 'react'; -import { ethers, Signer } from 'ethers'; +import { useEffect, useState } from "react"; +import { ethers, Signer } from "ethers"; //next imports -import Link from 'next/link' -import { useRouter } from 'next/router'; +import Link from "next/link"; +import { useRouter } from "next/router"; -import Provenance from '../../../artifacts/contracts/Provenance.sol/Provenance.json' +import Provenance from "../../../artifacts/contracts/Provenance.sol/Provenance.json"; //react-bootstrap imports -import { Container, Carousel, Table, Card, Image, Button} from 'react-bootstrap'; +import { + Container, + Carousel, + Table, + Card, + Image, + Button, +} from "react-bootstrap"; // context imports -import { useItemContext } from '../../../src/context/ItemContext'; -import { useContractContext } from '../../../src/context/ContractContext'; -import { useUserContext } from '../../../src/context/UserContext'; -import { useTransferContext } from '../../../src/context/TransferContext'; +import { useItemContext } from "../../../src/context/ItemContext"; +import { useContractContext } from "../../../src/context/ContractContext"; +import { useUserContext } from "../../../src/context/UserContext"; +import { useTransferContext } from "../../../src/context/TransferContext"; // styles -import styles from '../Search.module.css' +import styles from "../Search.module.css"; // components Imports -import { OfferModal } from '../../../src/components/OfferModal/OfferModal'; - -import ProvenanceHistory from '../../../src/components/ProvenanceHistory/ProvenanceHistory'; - +import { OfferModal } from "../../../src/components/OfferModal/OfferModal"; +import ProvenanceHistory from "../../../src/components/ProvenanceHistory/ProvenanceHistory"; const NonUserProvenance = () => { - const { ipfsGetterRootURL, bytes32ToString } = useItemContext(); const { TokenContract, MothershipContract } = useContractContext(); const { mainAccount, provider, signer } = useUserContext(); @@ -37,186 +41,195 @@ const NonUserProvenance = () => { const { nonUserProvenance } = router.query; + const [loaded, setLoaded] = useState(false); + const [provenanceContract, setProvenanceContract] = useState(); + const [provenanceProps, setProvenanceProps] = useState(); + const [provenanceOwnerInfo, setProvenanceOwnerInfo] = useState(); - - const [ loaded , setLoaded ] = useState(false); - const [ provenanceContract, setProvenanceContract ] = useState(); - const [ provenanceProps, setProvenanceProps ] = useState(); - const [ provenanceOwnerInfo, setProvenanceOwnerInfo ] = useState(); - - - - - //load all info - useEffect(async() => { + //load all info + useEffect(async () => { if (nonUserProvenance && signer) { - loadSingleProvenance() - .catch(error => console.log(error, 'populate error')); - } + loadSingleProvenance().catch((error) => + console.log(error, "populate error") + ); + } // would be faster search with an object not a for loop. not sure how that would work with the map though. - async function loadSingleProvenance() { - - - const SingleContract = new ethers.Contract(nonUserProvenance, Provenance.abi, signer); - - - const ProvenanceDetails = await SingleContract.instrument() - const itemPhotos = await SingleContract.getItemPics(); - const ProvenanceProps = {...ProvenanceDetails, itemPhotos} - - const index = SingleContract.ownerCount(); - const ProvenanceOwnerInfo = await SingleContract.ownerProvenance(index); - console.log(await SingleContract.getOwnershipHistory()) - - - - setProvenanceContract(SingleContract); - setProvenanceProps(ProvenanceProps); - setProvenanceOwnerInfo(ProvenanceOwnerInfo); - setLoaded(true) - + async function loadSingleProvenance() { + const SingleContract = new ethers.Contract( + nonUserProvenance, + Provenance.abi, + signer + ); + + const ProvenanceDetails = await SingleContract.instrument(); + const itemPhotos = await SingleContract.getItemPics(); + const ProvenanceProps = { ...ProvenanceDetails, itemPhotos }; + + const index = SingleContract.ownerCount(); + const ProvenanceOwnerInfo = await SingleContract.ownerProvenance(index); + console.log(await SingleContract.getOwnershipHistory()); + + setProvenanceContract(SingleContract); + setProvenanceProps(ProvenanceProps); + setProvenanceOwnerInfo(ProvenanceOwnerInfo); + setLoaded(true); } - },[nonUserProvenance, signer]) - + }, [nonUserProvenance, signer]); const ItemPhotoCarousel = () => { const itemPhotoArray = provenanceProps.itemPhotos; return ( -
+

Item Photos

- {itemPhotoArray.length > 0 ? - - {itemPhotoArray.map((photo, index)=> { - return ( - - item photos not yet loaded - - ) - })} - - - : + {itemPhotoArray.length > 0 ? ( + + {itemPhotoArray.map((photo, index) => { + return ( + + item photos not yet loaded + + ); + })} + + ) : (

Item Photos Loading

- +
- } - -
- ) - } + )} +
+ ); + }; const MakeOfferOnProvenance = () => { const [newOfferAmount, setNewOfferAmount] = useState(); const [currentOfferBuyer, setCurrentOfferBuyer] = useState(); const [currentOfferAmount, setCurrentOfferAmount] = useState(0); const [show, setShow] = useState(false); - const [offerMade, setOfferMade] = useState(false) - + const [offerMade, setOfferMade] = useState(false); - - useEffect(async() => { + useEffect(async () => { const result = await provenanceContract.currentOffer(); const buyer = result.buyer; const offer = result.offer; - + if (buyer !== ethers.constants.AddressZero) setCurrentOfferBuyer(buyer); - if (offer !== 0.0)setCurrentOfferAmount(ethers.utils.formatEther(offer)); - + if (offer !== 0.0) setCurrentOfferAmount(ethers.utils.formatEther(offer)); + if (offerMade) { setOfferMade(false); } - + }, [provenanceContract, offerMade]); - },[provenanceContract, offerMade]) - - const handleChange = event => { + const handleChange = (event) => { // console.log(event.target.value, "value") setNewOfferAmount(event.target.value); - } + }; const handleShow = () => setShow(true); const handleClose = () => setShow(false); - - async function approveToken() { - await TokenContract.approve(provenanceContract.address, provenanceProps.instrumentDeedToken.toString()). - then(result => console.log(result)) - .catch(error => console.log(error.data.message, "offer error")); + await TokenContract.approve( + provenanceContract.address, + provenanceProps.instrumentDeedToken.toString() + ) + .then((result) => console.log(result)) + .catch((error) => console.log(error.data.message, "offer error")); } - async function cancel() { - await provenanceContract.cancelOffer(). - then(result => console.log(result)) - .catch(error => console.log(error.data.message, "offer error")); + await provenanceContract + .cancelOffer() + .then((result) => console.log(result)) + .catch((error) => console.log(error.data.message, "offer error")); } async function accept() { - await provenanceContract.acceptOffer(). - then(result => console.log(result)) - .catch(error => console.log(error.data.message, "offer error")); + await provenanceContract + .acceptOffer() + .then((result) => console.log(result)) + .catch((error) => console.log(error.data.message, "offer error")); } - - return( + return (
- {provenanceOwnerInfo.ownerAddress === mainAccount ? + {provenanceOwnerInfo.ownerAddress === mainAccount ? (

Offers For This Provenance

- : + ) : (

Make Offer For this Provenance

- } + )} - {currentOfferBuyer ? + {currentOfferBuyer ? (
-

Current Offer By: {currentOfferBuyer} for {ethers.constants.EtherSymbol}{currentOfferAmount}

+

+ Current Offer By: {currentOfferBuyer} for{" "} + {ethers.constants.EtherSymbol} + {currentOfferAmount}{" "} +

- : + ) : (

No Current Offers on This Provenance

-
} +
+ )} - {(provenanceOwnerInfo.ownerAddress === mainAccount) && (currentOfferAmount !== (0 || undefined)) ? + {provenanceOwnerInfo.ownerAddress === mainAccount && + currentOfferAmount !== (0 || undefined) ? (
- - - + + +

this ***cannot*** be undone

- : + ) : (
-
- -
- - - -
} + +
+ + +
+ )}
- - ) - } - + ); + }; + // need logic here for loading and/or ownership of Contract - if (loaded && provenanceContract !== undefined) { - const { serial, brand, instrumentDeedToken, model, year, typeOfProvenance } = provenanceProps; + if (loaded && provenanceContract !== undefined) { + const { + serial, + brand, + instrumentDeedToken, + model, + year, + typeOfProvenance, + } = provenanceProps; const brandFormatted = bytes32ToString(brand); @@ -226,20 +239,27 @@ const NonUserProvenance = () => { return ( -
-

{brandFormatted} {modelFormatted}: {year}

+
+

+ {brandFormatted} {modelFormatted}: {year} +

Verification Photo Image

- -

Provenance Information

- + +

Provenance Information

+
- + @@ -266,34 +286,30 @@ const NonUserProvenance = () => { + +
Provenance Owner Address {provenanceOwnerInfo.ownerAddress}
Provenance Contract Address + Provenance Contract Address + {provenanceContract.address}
Serial Number {serialFormatted}
- - - - - - -
-

Address 2: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

- - -
- - - ) - - } else { - return( -
-

No Public Provenance at this Address

-

if you think you own this item please check in your items

+ + +
+

Address 2: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266

+
- - ) - } - -} - + + ); + } else { + return ( +
+

+ No Public Provenance at this Address +

+

if you think you own this item please check in your items

+
+ ); + } +}; export default NonUserProvenance; - diff --git a/pages/search/index.js b/pages/search/index.js index f52562f..46487f5 100644 --- a/pages/search/index.js +++ b/pages/search/index.js @@ -1,362 +1,392 @@ -import { useEffect, useState } from 'react'; -import { ethers } from 'ethers'; +import { useEffect, useState } from "react"; +import { ethers } from "ethers"; // context imports -import { useContractContext } from '../../src/context/ContractContext.js'; -import { useUserContext } from '../../src/context/UserContext.js'; -import { useItemContext } from '../../src/context/ItemContext.js'; +import { useContractContext } from "../../src/context/ContractContext.js"; +import { useUserContext } from "../../src/context/UserContext.js"; +import { useItemContext } from "../../src/context/ItemContext.js"; +import Provenance from "../../artifacts/contracts/Provenance.sol/Provenance.json"; -import Provenance from '../../artifacts/contracts/Provenance.sol/Provenance.json'; - -import PublicItemTable from '../../src/components/PublicItemTable/PublicItemTable.js'; +import PublicItemTable from "../../src/components/PublicItemTable/PublicItemTable.js"; // styles imports -import { Button, Container } from 'react-bootstrap'; -import { BeatLoader } from 'react-spinners'; +import { Button, Container } from "react-bootstrap"; +import { BeatLoader } from "react-spinners"; const initialValues = { - brand: "", - date: "", - instrumentDeedToken: '', - model: "", - ownerAddress: "", - ownerCount: '', - serial: "", - typeOfProvenance: '', - year: '' - }; + brand: "", + date: "", + instrumentDeedToken: "", + model: "", + ownerAddress: "", + ownerCount: "", + serial: "", + typeOfProvenance: "", + year: "", +}; function PublicProvenanceSearchTable() { + const { MothershipContract, TokenContract } = useContractContext(); + const { provider, signer } = useUserContext(); + const { bytes32ToString, stringToBytes32 } = useItemContext(); + + const [allProvenanceObjects, setAllProvenanceObjects] = useState(); + const [loading, setLoading] = useState(false); + + const [searchInput, setSearchInput] = useState([]); + const [values, setValues] = useState(initialValues); + const [filteredResults, setFilteredResults] = useState(); + + // ************************ + // TEMP FOR TESTING FILTERS + // *********************** + + const type = [0, 1, 2]; + const brands = [ + "Jupiter", + "Yamaha", + "JL Woodwinds", + "Yanigisawa", + "Antigua Winds", + "Pearl", + "Selmer", + "Buffet", + ]; + const models = [ + "Mark VI", + "SBA", + "R13", + "Bronze Series", + "Cigar Cutter", + "Balanced Action", + "King 20", + "Silver Fox", + ]; + const images = [ + "QmNvzkSMNCF9bRry5CHiTCnz7s8Fc6ooNVQyuFc4EPDaQV", + "QmPYABsoen4yRJWp4ta7yrhxgsqNEQLK15c68BL6BQrQAW", + "QmQ4wfPDcxJeLcypv6JQt6757Nmzi95k5wZbjjFYzjsUW2", + "QmdBEnkC1qXc1pZsGkRTPkwhqohGyiTd7tZKvD1v8VQ65Y", + ]; + + function serial() { + return Math.floor(Math.random() * 1000); + } - const { MothershipContract, TokenContract } = useContractContext(); - const { provider, signer } = useUserContext(); - const { bytes32ToString, stringToBytes32 } = useItemContext(); + function tokenID() { + return Math.floor(Math.random() * 100); + } - const [allProvenanceObjects, setAllProvenanceObjects] = useState(); - const [loading, setLoading] = useState(false); + function random_element(items) { + return items[Math.floor(Math.random() * items.length)]; + } - const [searchInput, setSearchInput] = useState([]); - const [values, setValues] = useState(initialValues); - const [filteredResults, setFilteredResults] = useState(); + function randomDate(start, end) { + const dateStr = new Date( + start.getTime() + Math.random() * (end.getTime() - start.getTime()) + ); + return dateStr.toLocaleDateString(); + } + function randomYear(start, end) { + return new Date( + start.getTime() + Math.random() * (end.getTime() - start.getTime()) + ); + } - // ************************ - // TEMP FOR TESTING FILTERS - // *********************** + const batchMintTokens = async () => { + await TokenContract.batchMint(100, random_element(images)); + }; - const type = [0, 1, 2] - const brands = ["Jupiter", "Yamaha", "JL Woodwinds", "Yanigisawa", "Antigua Winds", "Pearl", "Selmer", "Buffet"] - const models = ["Mark VI", "SBA", "R13", "Bronze Series", "Cigar Cutter", "Balanced Action", "King 20", "Silver Fox"] - const images = ["QmNvzkSMNCF9bRry5CHiTCnz7s8Fc6ooNVQyuFc4EPDaQV", "QmPYABsoen4yRJWp4ta7yrhxgsqNEQLK15c68BL6BQrQAW", "QmQ4wfPDcxJeLcypv6JQt6757Nmzi95k5wZbjjFYzjsUW2", "QmdBEnkC1qXc1pZsGkRTPkwhqohGyiTd7tZKvD1v8VQ65Y"] + const create10Provenances = async () => { + let type1 = await random_element(type); + let serial1 = stringToBytes32(serial().toString()); + let brand1 = await stringToBytes32(random_element(brands)); + let model1 = stringToBytes32(random_element(models)); + let year1 = randomYear(new Date(1897, 0, 1), new Date()).getFullYear(); + let tokenID1 = tokenID(); + let date1 = stringToBytes32( + randomDate(new Date(1897, 0, 1), new Date()).toString() + ); + + let vImages1 = random_element(images); + + MothershipContract.createBatchProvenances( + type1, + serial1, + brand1, + model1, + year1, + tokenID1, + date1, + vImages1, + images + ).then(async (result) => { + provider.waitForTransaction(result.hash).then((mined) => { + if (mined) { + MothershipContract.once("ProvenanceCreated", (type, newAddress) => { + console.log(`CreateBatchProvenances(11) a success!`); + }); + } + }); + }); + }; - function serial() { - return Math.floor(Math.random() * 1000); - } + // ********* + // END TEMP + // ********* - function tokenID() { - return Math.floor(Math.random() * 100) + useEffect(() => { + if (MothershipContract) { + loadAllProvenances() + /* + .then(setItemAdded(false)) + */ + .catch((error) => console.log(error, "populate loadAll error")); } - - function random_element(items) - { - return items[Math.floor(Math.random()*items.length)]; - } - - function randomDate(start, end) { - const dateStr = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); - return dateStr.toLocaleDateString(); + async function loadAllProvenances() { + setLoading(true); + let allProvenanceArray = []; + let addresses = await MothershipContract.getAllProvenances(); + + for (let address of addresses) { + const ProvenanceContract = new ethers.Contract( + address, + Provenance.abi, + signer + ); + const ProvenanceDetailsImport = await ProvenanceContract.instrument(); + + const ProvenanceDetails = structuredClone(ProvenanceDetailsImport); + + const { brand, model, serial } = ProvenanceDetails; + + ProvenanceDetails.brand = bytes32ToString(brand); + ProvenanceDetails.model = bytes32ToString(model); + ProvenanceDetails.serial = bytes32ToString(serial); + + const itemPhotos = await ProvenanceContract.getItemPics(); + const ProvenanceProps = { ...ProvenanceDetails, itemPhotos }; + + const index = ProvenanceContract.ownerCount(); + const ProvenanceOwnerInfo = await ProvenanceContract.ownerProvenance( + index + ); + + const ProvenancePendingOwner = await ProvenanceContract.pendingOwner(); + const ProvenanceCurrentOffer = await ProvenanceContract.currentOffer(); + + allProvenanceArray.push({ + ProvenanceContract: ProvenanceContract, + ProvenanceProps: ProvenanceProps, + ProvenanceOwnerInfo: ProvenanceOwnerInfo, + ProvenancePendingOwner: ProvenancePendingOwner, + ProvenanceCurrentOffer: ProvenanceCurrentOffer, + }); + } + setAllProvenanceObjects(allProvenanceArray); + setLoading(false); } - function randomYear(start, end) { - return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); - } - - const batchMintTokens = async() => { - await TokenContract.batchMint(100, random_element(images)) + }, [MothershipContract]); + useEffect(async () => { + if (values) { + searchItems(values); } + }, [values]); - const create10Provenances = async () => { - - - - let type1 = await random_element(type); - let serial1 = stringToBytes32((serial()).toString()); - let brand1 = await stringToBytes32(random_element(brands)); - let model1 = stringToBytes32(random_element(models)); - let year1 = (randomYear(new Date(1897, 0, 1), new Date())).getFullYear(); - let tokenID1 = tokenID(); - let date1 = stringToBytes32(((randomDate(new Date(1897, 0, 1), new Date()))).toString()); - - let vImages1 = random_element(images); - - - - - MothershipContract.createBatchProvenances( - type1, - serial1, - brand1, - model1, - year1, - tokenID1, - date1, - vImages1, - images) - .then(async(result) => { - provider.waitForTransaction(result.hash) - .then(mined => { - if (mined) { - MothershipContract.once("ProvenanceCreated", (type, newAddress) => { - - console.log(`CreateBatchProvenances(11) a success!`) - - })} - }) - - }) - - } - - // ********* - // END TEMP - // ********* - - useEffect(() => { - if (MothershipContract) { + const getAll = async () => { + const result = await MothershipContract.getAllProvenances(); + }; - loadAllProvenances() - /* - .then(setItemAdded(false)) - */ - .catch(error => console.log(error, 'populate loadAll error')); - } - async function loadAllProvenances() { - setLoading(true) - let allProvenanceArray = []; - let addresses = await MothershipContract.getAllProvenances(); - - for (let address of addresses) { - - const ProvenanceContract = new ethers.Contract(address, Provenance.abi, signer); - const ProvenanceDetailsImport = await ProvenanceContract.instrument(); - - const ProvenanceDetails = structuredClone(ProvenanceDetailsImport) - - const { brand, model, serial } = ProvenanceDetails; - - ProvenanceDetails.brand = bytes32ToString(brand) - ProvenanceDetails.model = bytes32ToString(model) - ProvenanceDetails.serial = bytes32ToString(serial) - - - const itemPhotos = await ProvenanceContract.getItemPics(); - const ProvenanceProps = {...ProvenanceDetails, itemPhotos} - - const index = ProvenanceContract.ownerCount(); - const ProvenanceOwnerInfo = await ProvenanceContract.ownerProvenance(index); - - const ProvenancePendingOwner = await ProvenanceContract.pendingOwner(); - const ProvenanceCurrentOffer = await ProvenanceContract.currentOffer(); - - allProvenanceArray.push({ - 'ProvenanceContract': ProvenanceContract, - 'ProvenanceProps': ProvenanceProps, - 'ProvenanceOwnerInfo': ProvenanceOwnerInfo, - 'ProvenancePendingOwner' : ProvenancePendingOwner, - 'ProvenanceCurrentOffer': ProvenanceCurrentOffer}) - - } - setAllProvenanceObjects(allProvenanceArray); - setLoading(false) - } - - - },[MothershipContract]) - - useEffect(async() => { - if (values){ - searchItems(values)} - }, [values]); - - - const getAll = async() => { - const result = await MothershipContract.getAllProvenances(); - } + const handleInputChange = (e) => { + const { name, value } = e.target; + setValues({ + ...values, + [name]: value, + }); + // searchItems(values); + }; - const handleInputChange = (e) => { - const { name, value } = e.target; + const handleInputChangeConvertToBytes = (e) => { + const { name, value } = e.target; - setValues({ - ...values, - [name]: value, - }); - // searchItems(values); - }; + setValues({ + ...values, + [name]: value, + }); + // searchItems(values); + }; - const handleInputChangeConvertToBytes = (e) => { - const { name, value } = e.target; + const searchItems = () => { + if (values !== initialValues) { + const filteredData = allProvenanceObjects.filter((item) => { + const itemProps = item.ProvenanceProps; + const itemOwnerInfo = item.ProvenanceOwnerInfo; + const combinedObj = { + ...itemProps, + ...itemOwnerInfo, + }; + + for (const [key, value] of Object.entries(values)) { + // console.log(Object.values((combinedObj[key]).toString()), "check") + if ( + value !== ("" || 0) && + !Object.values(combinedObj[key].toString()) + .join("") + .toLowerCase() + .includes(value.toString().toLowerCase()) + ) { + return false; + } + } + return true; + }); - setValues({ - ...values, - [name]: value, - }); - // searchItems(values); - }; - - - - const searchItems = () => { - - if (values !== initialValues) { - const filteredData = allProvenanceObjects.filter((item) => { - - const itemProps = item.ProvenanceProps; - const itemOwnerInfo = item.ProvenanceOwnerInfo; - const combinedObj = { - ...itemProps, - ...itemOwnerInfo - } - - for (const [key, value] of Object.entries(values)) { - // console.log(Object.values((combinedObj[key]).toString()), "check") - if ((value !== ('' || 0)) && !(Object.values((combinedObj[key]).toString()).join('').toLowerCase().includes((value.toString()).toLowerCase()))) { - return false; - } - } - return true; - - }) - - - - console.log(filteredData, "filteredData") - setFilteredResults(filteredData) - }else { - setFilteredResults(allProvenanceObjects) - }} - - const clearSearchForm = () => { - setValues(initialValues) - setFilteredResults(allProvenanceObjects) + console.log(filteredData, "filteredData"); + setFilteredResults(filteredData); + } else { + setFilteredResults(allProvenanceObjects); } + }; + const clearSearchForm = () => { + setValues(initialValues); + setFilteredResults(allProvenanceObjects); + }; -return ( + return ( <> -
- {loading ? - <> -

Contracts Loading...

- - : -

Search Provenances

- } -
- {/*
*/} -
- {/* */} - -
- - +
+ {loading ? ( + <> +

Contracts Loading...

+ + ) : ( +

Search Provenances

+ )} +
+ {/*
*/} +
+ {/* */} +
+ +

Brand

- +

Model

- -

Year

- + +

Year

+

Token

- +

Serial

- +

Owner Address

- +

Type of Provenance

- +
+ +
+ +
+
+ {loading ? ( +
+ +
+ ) : ( +
+

Green Bordered Provenances Owned By You

+

+ Red Bordered Provenances Are Incoming Provenances You Need To + Accept +

+

Purple Bordered Provenances have open offers

+ -
- - - - -
-
- {loading ? -
- -
- : -
-

Green Bordered Provenances Owned By You

-

Red Bordered Provenances Are Incoming Provenances You Need To Accept

-

Purple Bordered Provenances have open offers

- -
- - } -
-
- - +
+ )} +
+
+ + -
- - +
+ ); +} -) - - - } - - - export default PublicProvenanceSearchTable; - - - +export default PublicProvenanceSearchTable; // if (!(isEmptyObject(values))) { // const filteredData = allProvenanceObjects.filter((item) => { @@ -369,17 +399,16 @@ return ( // const valuesKeys = Object.keys(values); // const combinedObjKeys = Object.keys(combinedObj); // for (const [key, value] of Object.entries(values)) { - + // console.log(key, value) // return Object.values(combinedObj).join('').toLowerCase().includes(value.toLowerCase()); - - + // // console.log(Object.entries(values), "object entires") // // console.log(combinedObj, "combinedObj") // // return Object.values(combinedObj).join('').toLowerCase().includes(value.toLowerCase()); // } // }) - + // // const filteredData = allProvenanceObjects.filter(function(item) { // // for (const [key, value] of Object.entries(values)) { // // console.log(key, "key") @@ -388,8 +417,7 @@ return ( // // } // // return true; // // }); - // console.log(filteredData, "filteredData") // setFilteredResults(filteredData) -// } \ No newline at end of file +// } diff --git a/pages/test-static.js b/pages/test-static.js index 2ad765f..c54cee3 100644 --- a/pages/test-static.js +++ b/pages/test-static.js @@ -1,44 +1,45 @@ import { Pagination } from "react-bootstrap"; function ListPageComponent(props) { - console.log(props) - return ( -
-

List of Folders

-
    - {props.lists.map(list =>
  • {list.name}
  • )} -
- - - - {1} - + console.log(props); + return ( +
+

List of Folders

+
    + {props.lists.map((list) => ( +
  • {list.name}
  • + ))} +
+ + + + {1} + - {10} - {11} - {12} - {13} - {14} + {10} + {11} + {12} + {13} + {14} - - {20} - - - -
- ) + + {20} + + +
+
+ ); } export async function getStaticProps() { - - return { - props: { - lists: [ - {dirId: '1', name: 'account.toString()'}, - {dirId: '2', name: 'Directory 2'}, - {dirId: '3', name: 'Directory 3'}, - {dirId: '4', name: 'Directory 4'} - ], - } - } + return { + props: { + lists: [ + { dirId: "1", name: "account.toString()" }, + { dirId: "2", name: "Directory 2" }, + { dirId: "3", name: "Directory 3" }, + { dirId: "4", name: "Directory 4" }, + ], + }, + }; } -export default ListPageComponent; \ No newline at end of file +export default ListPageComponent; diff --git a/scripts/deployInstrumentToken.js b/scripts/deployInstrumentToken.js index d9206d7..3ba6c68 100644 --- a/scripts/deployInstrumentToken.js +++ b/scripts/deployInstrumentToken.js @@ -1,26 +1,29 @@ -const fs = require('fs') +const fs = require("fs"); async function main() { + const InstrumentDeedToken = await ethers.getContractFactory( + "InstrumentDeedToken" + ); + const instrumentdeedtoken = await InstrumentDeedToken.deploy(); - const InstrumentDeedToken = await ethers.getContractFactory("InstrumentDeedToken"); - const instrumentdeedtoken = await InstrumentDeedToken.deploy(); - - - - fs.writeFileSync('./src/deployedContractAddresses/instrumenttokenaddress.json', `{ "address": "${instrumentdeedtoken.address}" }`, (err) => { + fs.writeFileSync( + "./src/deployedContractAddresses/instrumenttokenaddress.json", + `{ "address": "${instrumentdeedtoken.address}" }`, + (err) => { if (err) { - console.log(err, "error") - } else { - console.log("InstrumentDeedToken address written successfully\n"); + console.log(err, "error"); + } else { + console.log("InstrumentDeedToken address written successfully\n"); } - }) + } + ); + + console.log("InstrumentDeedToken deployed to:", instrumentdeedtoken.address); +} - console.log("InstrumentDeedToken deployed to:", instrumentdeedtoken.address); - } - - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deployMothership.js b/scripts/deployMothership.js index 21870d3..37ad61b 100644 --- a/scripts/deployMothership.js +++ b/scripts/deployMothership.js @@ -1,37 +1,34 @@ -const fs = require('fs') +const fs = require("fs"); - -const token = require('../src/deployedContractAddresses/instrumenttokenaddress.json').address +const token = + require("../src/deployedContractAddresses/instrumenttokenaddress.json").address; async function main() { - - - const Mothership = await ethers.getContractFactory("Mothership"); - - //pass token address into Mothership constructor - const mothership = await Mothership.deploy(token); - - console.log("Mothership deployed to:", mothership.address); - - - fs.writeFileSync('./src/deployedContractAddresses/mothershipaddress.json', `{ "address": "${mothership.address}" }`, (err) => { - if (err) console.log(err); else { console.log("Mothership address written successfully\n"); } - }) - - - } - - - - main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); - - - + const Mothership = await ethers.getContractFactory("Mothership"); + + //pass token address into Mothership constructor + const mothership = await Mothership.deploy(token); + + console.log("Mothership deployed to:", mothership.address); + + fs.writeFileSync( + "./src/deployedContractAddresses/mothershipaddress.json", + `{ "address": "${mothership.address}" }`, + (err) => { + if (err) console.log(err); + else { + console.log("Mothership address written successfully\n"); + } + } + ); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); /* @@ -56,5 +53,3 @@ async function main() { }) */ - - \ No newline at end of file diff --git a/src/components/ConfirmationModal.js b/src/components/ConfirmationModal.js index 6425fb2..5f69d3f 100644 --- a/src/components/ConfirmationModal.js +++ b/src/components/ConfirmationModal.js @@ -1,138 +1,154 @@ -import { Col, Modal, Image, Button, Container, Row, Table, Carousel, Spinner} from 'react-bootstrap'; +import { + Col, + Modal, + Image, + Button, + Container, + Row, + Table, + Carousel, + Spinner, +} from "react-bootstrap"; //import './ConfirmationModal.css'; -import waitingkitten from '../../public/images/waitingkitten.jpeg'; +import waitingkitten from "../../public/images/waitingkitten.jpeg"; -import { useUserContext } from '../context/UserContext'; +import { useUserContext } from "../context/UserContext"; -import styles from './ConfirmationModal.module.css'; +import styles from "./ConfirmationModal.module.css"; export function ConfirmationModal(props) { + const { + createpracticeprovenance, + createprovenance, + setSubmitting, + formdata, + unusedtokenid, + ...restOfProps + } = props; + + const { ipfsGetterRootURL } = useUserContext(); + + const verificationPhotoURL = + ipfsGetterRootURL + props.formdata.verificationphotohash; + + const DataTable = () => { + let item = ""; + + if (formdata.type === 0) { + item = "Instrument"; + } else if (formdata.type === 1) { + item = "Accessory"; + } else { + item = "Gear"; + } - const {createpracticeprovenance, createprovenance, setSubmitting, formdata, unusedtokenid, ...restOfProps} = props; - - const { ipfsGetterRootURL } = useUserContext(); - - const verificationPhotoURL = ipfsGetterRootURL + props.formdata.verificationphotohash; - - const DataTable = () => { - let item = ''; - - if (formdata.type === 0) { - item = 'Instrument' - } else if (formdata.type === 1) { - item = 'Accessory' - } else { - item = 'Gear' - } - - return ( - - <> - - - - - - - - - - - - - - - - - - - - - - - -
Token ID:{unusedtokenid}
Type of Item: {item}
Model Name: {formdata.model}{formdata.model}
Year Manufactured:{formdata.year}
Serial Number: {formdata.serial}{formdata.serial}
- - - - ) - } - - const ItemImageCarousel = () => { - - return ( -
- - {props.formdata.instrumentphotohashes && (props.formdata.instrumentphotohashes).length > 0 ? - - {(formdata.instrumentphotohashes).map((photo, index)=> { - return ( - - item photos not yet loaded - - - ) - })} - - - : -
-

Item Photos Loading

- -
- } - -
- ) - } - - + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + +
Token ID:{unusedtokenid}
Type of Item: {item}
Model Name: {formdata.model}{formdata.model}
Year Manufactured:{formdata.year}
Serial Number: {formdata.serial}{formdata.serial}
+ + ); + }; - + const ItemImageCarousel = () => { return ( - + {props.formdata.instrumentphotohashes && + props.formdata.instrumentphotohashes.length > 0 ? ( + + {formdata.instrumentphotohashes.map((photo, index) => { + return ( + + item photos not yet loaded + + ); + })} + + ) : ( +
+

Item Photos Loading

+ +
+ )} +
+ ); + }; + + return ( + + + - - Please Confirm All Info Below Before Provenance Creation - - - - - - -

Token Verification Image

- verification photo not yet uploaded - - -

Provenance Details

-
- - -
- -
- - -
-
- -
- ); - } - - \ No newline at end of file + onClick={createprovenance} + > + Create Provenance + + + ); +} diff --git a/src/components/ConfirmationModal.module.css b/src/components/ConfirmationModal.module.css index f028b97..a91d224 100644 --- a/src/components/ConfirmationModal.module.css +++ b/src/components/ConfirmationModal.module.css @@ -1,60 +1,56 @@ - .spinner-border { - object-fit: cover; + object-fit: cover; } -@media (min-width: 992px){ -.modal-lg, .modal-xl { +@media (min-width: 992px) { + .modal-lg, + .modal-xl { max-width: 90%; -}} - + } +} .carouselItem { - height: 180px; - display: block; - margin-left: auto; - margin-right: auto; + height: 180px; + display: block; + margin-left: auto; + margin-right: auto; } .itemPhotoCarousel { - height: 300px; + height: 300px; } - .carousel-control-prev-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E") !important; - } - + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E") !important; +} + .carousel-control-next-icon { - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E") !important; - } + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23000' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E") !important; +} .carousel-indicators [data-bs-target] { -box-sizing: content-box; -flex: 0 1 auto; -width: 30px; -height: 3px; -padding: 0; -margin-right: 3px; -margin-left: 3px; -text-indent: -999px; -cursor: pointer; -background-color: gray; -background-clip: padding-box; -border: 0; -border-top: 10px solid transparent; -border-bottom: 10px solid transparent; -opacity: .5; -transition: opacity .6s ease; + box-sizing: content-box; + flex: 0 1 auto; + width: 30px; + height: 3px; + padding: 0; + margin-right: 3px; + margin-left: 3px; + text-indent: -999px; + cursor: pointer; + background-color: gray; + background-clip: padding-box; + border: 0; + border-top: 10px solid transparent; + border-bottom: 10px solid transparent; + opacity: 0.5; + transition: opacity 0.6s ease; } - .createProvenanceButton { - font-size: 200%; - margin: 0 auto; - margin-bottom: 3px; - border-radius: 25px; - width: 80%; + font-size: 200%; + margin: 0 auto; + margin-bottom: 3px; + border-radius: 25px; + width: 80%; } - - \ No newline at end of file diff --git a/src/components/ConnectWalletButton.js b/src/components/ConnectWalletButton.js index 6404be3..bc72430 100644 --- a/src/components/ConnectWalletButton.js +++ b/src/components/ConnectWalletButton.js @@ -1,56 +1,73 @@ -import { useState , React, useEffect } from 'react'; +import { useState, React, useEffect } from "react"; - -import { ethers } from 'ethers'; -import { useUserContext } from '../context/UserContext'; +import { ethers } from "ethers"; +import { useUserContext } from "../context/UserContext"; // images -import greenCheckMark from '../../public/images/green_checkmark.png'; - +import greenCheckMark from "../../public/images/green_checkmark.png"; // styles -import { Button, Image, Dropdown } from 'react-bootstrap'; -import styles from './ConnectWalletButton.module.css'; -import { truncateAddress } from '../hooks/utils'; +import { Button, Image, Dropdown } from "react-bootstrap"; +import styles from "./ConnectWalletButton.module.css"; +import { truncateAddress } from "../hooks/utils"; const ConnectWalletButton = () => { - - const { mainAccount, setMainAccount, connectionErrorMessage, setConnectionErrorMessage, chainId, disconnect, connectWallet } = useUserContext(); - - useEffect(()=> { - if (mainAccount) setConnectionErrorMessage(''); - },[mainAccount]) - - return ( -
- {mainAccount ? - - - {`Account: ${truncateAddress(mainAccount)}`} - { + if (mainAccount) setConnectionErrorMessage(""); + }, [mainAccount]); + + return ( +
+ {mainAccount ? ( + + + {`Account: ${truncateAddress(mainAccount)}`} + checkmark - - - - {`Chain Id: ${chainId}`} - Disconnect Account - - - - : - - } - {connectionErrorMessage ?

{connectionErrorMessage}

: null} - - -
- ) -} + src={greenCheckMark.src} + alt="checkmark" + /> +
+ + + {`Chain Id: ${chainId}`} + + Disconnect Account + + +
+ ) : ( + + )} + {connectionErrorMessage ? ( +

+ {connectionErrorMessage} +

+ ) : null} +
+ ); +}; export default ConnectWalletButton; diff --git a/src/components/ConnectWalletButton.module.css b/src/components/ConnectWalletButton.module.css index 7e7deaf..b433ab7 100644 --- a/src/components/ConnectWalletButton.module.css +++ b/src/components/ConnectWalletButton.module.css @@ -1,36 +1,36 @@ .button { - border-radius: 12px; - border-color: lightskyblue; - color: white; - padding: 15px 32px; - text-align: center; - font-size: 15px; - max-width: 30%; - max-height: 100px; - font-weight: bold; - float: right; - } + border-radius: 12px; + border-color: lightskyblue; + color: white; + padding: 15px 32px; + text-align: center; + font-size: 15px; + max-width: 30%; + max-height: 100px; + font-weight: bold; + float: right; +} - .SuccessButton { - border-radius: 12px; - color: #fff; - background-color: #55a0f0; - border-color: #55a0f0; - padding: 15px 32px; - text-align: center; - font-size: 15px; - max-width: 250px; - max-height: 100px; - font-weight: bold; - float: right; - } +.SuccessButton { + border-radius: 12px; + color: #fff; + background-color: #55a0f0; + border-color: #55a0f0; + padding: 15px 32px; + text-align: center; + font-size: 15px; + max-width: 250px; + max-height: 100px; + font-weight: bold; + float: right; +} - .checkmarkImage { - width: 10%; - margin-left: 5px; - } +.checkmarkImage { + width: 10%; + margin-left: 5px; +} - .buttonText { - font-size: 25px; - margin: 0 auto; - } \ No newline at end of file +.buttonText { + font-size: 25px; + margin: 0 auto; +} diff --git a/src/components/DragAndDrop.css b/src/components/DragAndDrop.css index ddac294..f5de1dd 100644 --- a/src/components/DragAndDrop.css +++ b/src/components/DragAndDrop.css @@ -1,6 +1,6 @@ .file-input { display: none; -} +} .file-upload-btn { color: white; @@ -29,7 +29,7 @@ .upload-icon { width: 50px; height: 50px; - background: url(../../public/images/upload.png) no-repeat center center; + background: url(../../public/images/upload.png) no-repeat center center; background-size: 100%; text-align: center; margin: 0 auto; @@ -45,25 +45,25 @@ .file-progressBar { width: 100%; - border-radius: 5px; + border-radius: 5px; overflow: hidden; - display:inline-block; - margin:0px 10px 5px 5px; - vertical-align:top; + display: inline-block; + margin: 0px 10px 5px 5px; + vertical-align: top; flex: 6 !important; margin-left: 50px; box-sizing: content-box; - width:calc(100% - 50px) + width: calc(100% - 50px); } - + .file-progressBar div { height: auto; color: #fff; text-align: right; line-height: 15px; width: 0; - background-color: #4caf50; - border-radius: 3px; + background-color: #4caf50; + border-radius: 3px; font-size: 13px; } @@ -73,9 +73,9 @@ width: 800px; } -.file-status-bar{ +.file-status-bar { width: 100%; - vertical-align:top; + vertical-align: top; margin-top: 10px; margin-bottom: 20px; position: relative; @@ -88,7 +88,7 @@ } .file-type { - display: inline-block!important; + display: inline-block !important; position: absolute; font-size: 12px; font-weight: 700; @@ -104,7 +104,7 @@ .file-name { display: inline-block; - vertical-align:top; + vertical-align: top; margin-left: 50px; color: #4aa1f3; } @@ -123,24 +123,24 @@ .file-type-logo { width: 50px; height: 50px; - background: url(../../public/images/generic.png) no-repeat center center; + background: url(../../public/images/generic.png) no-repeat center center; background-size: 100%; position: absolute; } .file-size { - display:inline-block; - vertical-align:top; - color:#30693D; - margin-left:10px; - margin-right:5px; + display: inline-block; + vertical-align: top; + color: #30693d; + margin-left: 10px; + margin-right: 5px; margin-left: 10px; color: #444242; font-weight: 700; font-size: 14px; } -.file-remove { +.file-remove { position: absolute; top: 20px; right: 10px; @@ -150,26 +150,26 @@ margin-right: -10px; } -.modal{ +.modal { z-index: 999; display: none; overflow: hidden; } -.modal .overlay{ +.modal .overlay { width: 100%; height: 100vh; - background: rgba(0,0,0,.66); + background: rgba(0, 0, 0, 0.66); position: absolute; top: 0; left: 0; } -.modal .modal-image{ +.modal .modal-image { position: absolute; top: 50%; left: 50%; - transform: translate(-50%,-50%); + transform: translate(-50%, -50%); overflow: hidden; object-fit: cover; width: 100%; @@ -184,7 +184,7 @@ color: red; top: 50%; left: 50%; - transform: translate(-50%,-50%); + transform: translate(-50%, -50%); font-size: 16px; } @@ -204,10 +204,10 @@ overflow: hidden; } -.upload-modal .overlay{ +.upload-modal .overlay { width: 100%; height: 100vh; - background: rgba(0,0,0,.66); + background: rgba(0, 0, 0, 0.66); position: absolute; top: 0; left: 0; @@ -220,7 +220,7 @@ position: absolute; top: 50%; left: 50%; - transform: translate(-50%,-50%); + transform: translate(-50%, -50%); overflow: hidden; } @@ -236,7 +236,7 @@ position: absolute; top: 50%; left: 50%; - transform: translate(-50%,-50%); + transform: translate(-50%, -50%); background-color: #efefef; height: 20px; border-radius: 5px; @@ -255,4 +255,3 @@ .error { color: red; } - diff --git a/src/components/DragAndDrop.js b/src/components/DragAndDrop.js index c80f6ac..25426c3 100644 --- a/src/components/DragAndDrop.js +++ b/src/components/DragAndDrop.js @@ -1,110 +1,109 @@ -import React, { useRef, useState, useEffect, forwardRef } from 'react'; -import axios from 'axios'; -import { Spinner, Button } from 'react-bootstrap'; +import React, { useRef, useState, useEffect, forwardRef } from "react"; +import axios from "axios"; +import { Spinner, Button } from "react-bootstrap"; -import Image from 'next/image'; +import Image from "next/image"; -import waitingkitten from '../../public/images/waitingkitten.jpeg' +import waitingkitten from "../../public/images/waitingkitten.jpeg"; //import './DragAndDrop.css'; const DragAndDrop = ({ - photoLimit, - formDataImport, - setReadyToMint, - setMintErrorMessage, - setFormData, - itemPhotosUploaded, - setItemPhotosUploaded, - claimPhoto}) => { - - const FormData = require('form-data'); - - const fileInputRef = useRef(); - const modalImageRef = useRef(); - const modalRef = useRef(); - const progressRef = useRef(); - const uploadRef = useRef(); - const uploadModalRef = useRef(); - - const [selectedFiles, setSelectedFiles] = useState([]); - const [validFiles, setValidFiles] = useState([]); - const [unsupportedFiles, setUnsupportedFiles] = useState([]); - - const [loading, setLoading] = useState(''); - - - /* + photoLimit, + formDataImport, + setReadyToMint, + setMintErrorMessage, + setFormData, + itemPhotosUploaded, + setItemPhotosUploaded, + claimPhoto, +}) => { + const FormData = require("form-data"); + + const fileInputRef = useRef(); + const modalImageRef = useRef(); + const modalRef = useRef(); + const progressRef = useRef(); + const uploadRef = useRef(); + const uploadModalRef = useRef(); + + const [selectedFiles, setSelectedFiles] = useState([]); + const [validFiles, setValidFiles] = useState([]); + const [unsupportedFiles, setUnsupportedFiles] = useState([]); + + const [loading, setLoading] = useState(""); + + /* const [tokenMinted, setTokenMinted] = useState(false); const [tokenID, setTokenID] = useState(0); */ - - const [photoLimitMessage, setPhotoLimitMessage] = useState(false); - const [itemPhotoLimitError, setItemPhotoLimitError] = useState(''); - const [errorMessage, setErrorMessage] = useState(false); - - useEffect(() => { - let filteredArr = selectedFiles.reduce((acc, current) => { - const x = acc.find(item => item.name === current.name); - if (!x) { - return acc.concat([current]); - } else { - return acc; - } - }, []); - setValidFiles([...filteredArr]); - - }, [selectedFiles]); - - - - //drag ops - - const preventDefault = (e) => { - e.preventDefault(); - // e.stopPropagation(); - } - - const dragOver = (e) => { - preventDefault(e); - } - - const dragEnter = (e) => { - preventDefault(e); - } - - const dragLeave = (e) => { - preventDefault(e); + const [photoLimitMessage, setPhotoLimitMessage] = useState(false); + const [itemPhotoLimitError, setItemPhotoLimitError] = useState(""); + const [errorMessage, setErrorMessage] = useState(false); + + useEffect(() => { + let filteredArr = selectedFiles.reduce((acc, current) => { + const x = acc.find((item) => item.name === current.name); + if (!x) { + return acc.concat([current]); + } else { + return acc; + } + }, []); + setValidFiles([...filteredArr]); + }, [selectedFiles]); + + //drag ops + + const preventDefault = (e) => { + e.preventDefault(); + // e.stopPropagation(); + }; + + const dragOver = (e) => { + preventDefault(e); + }; + + const dragEnter = (e) => { + preventDefault(e); + }; + + const dragLeave = (e) => { + preventDefault(e); + }; + + const fileDrop = (e) => { + preventDefault(e); + const files = e.dataTransfer.files; + + if (files.length) { + handleFiles(files); } - - const fileDrop = (e) => { - preventDefault(e); - const files = e.dataTransfer.files; - - - if (files.length) { - handleFiles(files); - } + }; + + const fileInputClicked = () => { + fileInputRef.current.click(); + }; + + //File Selection and Validation + + const validateFile = (file) => { + const validTypes = [ + "image/jpeg", + "image/jpg", + "image/png", + "image/gif", + "image/x-icon", + ]; + if (validTypes.indexOf(file.type) === -1) { + return false; } - const fileInputClicked = () => { - fileInputRef.current.click(); - } - - //File Selection and Validation - - - const validateFile = (file) => { - const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/x-icon']; - if (validTypes.indexOf(file.type) === -1) { - return false; - } - - return true; - } -/* + return true; + }; + /* Original to save until deletion const handleFiles = (files) => { @@ -120,7 +119,7 @@ const DragAndDrop = ({ } } */ - /* + /* const handleFiles = (files) => { @@ -145,290 +144,336 @@ const DragAndDrop = ({ } */ - - const handleFiles = (files) => { - - if (photoLimit === 20 && files.length >= 20) { - setItemPhotoLimitError('Please limit items photos to 20. Handle Files') - } - else if (photoLimit === 1 && files.length > 1) { - if (validateFile(files[0])) { - setSelectedFiles(prevArray => [...prevArray, files[0]]) + const handleFiles = (files) => { + if (photoLimit === 20 && files.length >= 20) { + setItemPhotoLimitError("Please limit items photos to 20. Handle Files"); + } else if (photoLimit === 1 && files.length > 1) { + if (validateFile(files[0])) { + setSelectedFiles((prevArray) => [...prevArray, files[0]]); + } + setPhotoLimitMessage("Please upload just one verification photo."); + console.log(files.length, "file length"); + } else { + for (let i = 0; i < files.length; i++) { + if (validateFile(files[i])) { + if (photoLimit === 1 && validFiles.length < 1) { + setSelectedFiles((prevArray) => [...prevArray, files[i]]); + if (!claimPhoto) { + setReadyToMint(true); } - setPhotoLimitMessage('Please upload just one verification photo.') - console.log(files.length, "file length") + } else if (photoLimit === 20 && validFiles.length < 20) { + setSelectedFiles((prevArray) => [...prevArray, files[i]]); + setReadyToMint(false); + } else if (photoLimit === 20 && validFiles.length >= 20) { + setPhotoLimitMessage( + "Maximum file limit reached (20) for instrument photos." + ); + } else if (photoLimit === 1 && validFiles.length > 0) { + setPhotoLimitMessage("Please upload just one verification photo."); + } } else { - - for(let i = 0; i < files.length; i++) { - if (validateFile(files[i])) { - if (photoLimit === 1 && validFiles.length < 1) { - setSelectedFiles(prevArray => [...prevArray, files[i]]); - if (!claimPhoto) { - setReadyToMint(true) - } - } else if (photoLimit === 20 && validFiles.length < 20) { - setSelectedFiles(prevArray => [...prevArray, files[i]]); - setReadyToMint(false) - - } else if (photoLimit === 20 && validFiles.length >= 20) { - setPhotoLimitMessage('Maximum file limit reached (20) for instrument photos.') - } - else if (photoLimit === 1 && validFiles.length > 0) { - setPhotoLimitMessage('Please upload just one verification photo.') - } - - } else { - files[i]['invalid'] = true; - setSelectedFiles(prevArray => [...prevArray, files[i]]); - setErrorMessage('File type not permitted'); - setUnsupportedFiles(prevArray => [...prevArray, files[i]]); - } - } - } - - } - - const filesSelected = () => { - if (fileInputRef.current.files.length) { - handleFiles(fileInputRef.current.files); + files[i]["invalid"] = true; + setSelectedFiles((prevArray) => [...prevArray, files[i]]); + setErrorMessage("File type not permitted"); + setUnsupportedFiles((prevArray) => [...prevArray, files[i]]); } + } } + }; - - const fileSize = (size) => { - if (size === 0) { - return '0 Bytes'; - } - const k = 1024; - const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; - const i = Math.floor(Math.log(size) / Math.log(k)); - return parseFloat((size / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; + const filesSelected = () => { + if (fileInputRef.current.files.length) { + handleFiles(fileInputRef.current.files); } + }; - const fileType = (fileName) => { - return fileName.substring(fileName.lastIndexOf('.') + 1, fileName.length) || fileName; + const fileSize = (size) => { + if (size === 0) { + return "0 Bytes"; } + const k = 1024; + const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; + const i = Math.floor(Math.log(size) / Math.log(k)); + return parseFloat((size / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]; + }; - const removeFile = (name) => { - const index = validFiles.findIndex(e => e.name === name); - const index2 = selectedFiles.findIndex(e => e.name === name); - const index3 = unsupportedFiles.findIndex(e => e.name === name); - validFiles.splice(index, 1); - selectedFiles.splice(index2, 1); - setValidFiles([...validFiles]); - setSelectedFiles([...selectedFiles]); - if (index3 !== -1) { - unsupportedFiles.splice(index3, 1); - setUnsupportedFiles([...unsupportedFiles]); - } - - if (validFiles.length <= photoLimit) { - setPhotoLimitMessage('') - } - - if (photoLimit === 1 && validFiles.length === 0) { - setReadyToMint(false) - setMintErrorMessage(''); - - } + const fileType = (fileName) => { + return ( + fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length) || + fileName + ); + }; + + const removeFile = (name) => { + const index = validFiles.findIndex((e) => e.name === name); + const index2 = selectedFiles.findIndex((e) => e.name === name); + const index3 = unsupportedFiles.findIndex((e) => e.name === name); + validFiles.splice(index, 1); + selectedFiles.splice(index2, 1); + setValidFiles([...validFiles]); + setSelectedFiles([...selectedFiles]); + if (index3 !== -1) { + unsupportedFiles.splice(index3, 1); + setUnsupportedFiles([...unsupportedFiles]); } - //Image modal - - const openImageModal = (file) => { - const reader = new FileReader(); - modalRef.current.style.display = "block"; - reader.readAsDataURL(file); - reader.onload = function(e) { - modalImageRef.current.style.backgroundImage = `url(${e.target.result})`; - } + if (validFiles.length <= photoLimit) { + setPhotoLimitMessage(""); } - const closeModal = () => { - modalRef.current.style.display = "none"; - modalImageRef.current.style.backgroundImage = 'none'; - + if (photoLimit === 1 && validFiles.length === 0) { + setReadyToMint(false); + setMintErrorMessage(""); } - - - const uploadFiles = async() => { - event.preventDefault(); - setLoading(true); - let instrumentPhotoHashesArray = []; - const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`; - - uploadModalRef.current.style.display = 'block'; - //uploadRef.current.innerHTML = 'File(s) Uploading...'; - - Promise.all(validFiles.map((validFile) => { - const formData = new FormData(); - formData.append('file', validFile); - - return axios.post( url , formData, { - maxBodyLength : 'Infinity', - headers: { - pinata_api_key: `${process.env.NEXT_PUBLIC_PINATA_API_KEY}`, - pinata_secret_api_key: `${process.env.NEXT_PUBLIC_PINATA_SECRET_API_KEY}` - }})}) - ) - .then((response) => { - - //uploadRef.current.innerHTML = 'File(s) Uploaded' - setLoading(false) - - validFiles.length = 0; - setValidFiles([...validFiles]); - setSelectedFiles([...validFiles]); - setUnsupportedFiles([...validFiles]); - uploadModalRef.current.style.display = 'none'; - for (let responseObject of response) { - - if (photoLimit === 1) { - setFormData({ - name: 'verificationphotohash', - value: responseObject.data.IpfsHash}) - //console.log(response.data.IpfsHash, "hitting photolimit 1") - setPhotoLimitMessage(''); - setReadyToMint(true) - } else { - //axios.all - - instrumentPhotoHashesArray.push(responseObject.data.IpfsHash); - setFormData({ - name: 'instrumentphotohashes', - value: instrumentPhotoHashesArray}) - setItemPhotosUploaded(true) - } - - - } - }) - .catch(() => { - uploadRef.current.innerHTML = `Error Uploading File(s)`; - //undefined error keeps throwing here - progressRef.current.style.backgroundColor = 'red'; - }) - .catch((error) => console.log(error)) - + }; + + //Image modal + + const openImageModal = (file) => { + const reader = new FileReader(); + modalRef.current.style.display = "block"; + reader.readAsDataURL(file); + reader.onload = function (e) { + modalImageRef.current.style.backgroundImage = `url(${e.target.result})`; + }; + }; + + const closeModal = () => { + modalRef.current.style.display = "none"; + modalImageRef.current.style.backgroundImage = "none"; + }; + + const uploadFiles = async () => { + event.preventDefault(); + setLoading(true); + let instrumentPhotoHashesArray = []; + const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`; + + uploadModalRef.current.style.display = "block"; + //uploadRef.current.innerHTML = 'File(s) Uploading...'; + + Promise.all( + validFiles.map((validFile) => { + const formData = new FormData(); + formData.append("file", validFile); + + return axios.post(url, formData, { + maxBodyLength: "Infinity", + headers: { + pinata_api_key: `${process.env.NEXT_PUBLIC_PINATA_API_KEY}`, + pinata_secret_api_key: `${process.env.NEXT_PUBLIC_PINATA_SECRET_API_KEY}`, + }, + }); + }) + ) + .then((response) => { + //uploadRef.current.innerHTML = 'File(s) Uploaded' + setLoading(false); + + validFiles.length = 0; + setValidFiles([...validFiles]); + setSelectedFiles([...validFiles]); + setUnsupportedFiles([...validFiles]); + uploadModalRef.current.style.display = "none"; + for (let responseObject of response) { + if (photoLimit === 1) { + setFormData({ + name: "verificationphotohash", + value: responseObject.data.IpfsHash, + }); + //console.log(response.data.IpfsHash, "hitting photolimit 1") + setPhotoLimitMessage(""); + setReadyToMint(true); + } else { + //axios.all + + instrumentPhotoHashesArray.push(responseObject.data.IpfsHash); + setFormData({ + name: "instrumentphotohashes", + value: instrumentPhotoHashesArray, + }); + setItemPhotosUploaded(true); + } + } + }) + .catch(() => { + uploadRef.current.innerHTML = `Error Uploading File(s)`; + //undefined error keeps throwing here + progressRef.current.style.backgroundColor = "red"; + }) + .catch((error) => console.log(error)); + }; + + const closeUploadModal = () => { + uploadModalRef.current.style.display = "none"; + }; + + const PhotoUploadMessage = () => { + let photoMsg; + if (photoLimit === 1) { + const msg = + formDataImport.verificationphotohash.length > 0 + ? "Verication Photo Successfully Uploaded. Upload Another Photo to Change" + : "Upload Your Verification Photo to Mint Token"; + photoMsg = msg; } - - - const closeUploadModal = () => { - uploadModalRef.current.style.display = 'none'; + if (photoLimit === 1 && claimPhoto) { + const msg = + formDataImport.verificationphotohash.length > 0 + ? "Verication Photo Successfully Uploaded. Upload Another Photo to Change" + : "Upload Your Verification Photo to Claim Provenance"; + photoMsg = msg; } - const PhotoUploadMessage =() => { - let photoMsg; - if (photoLimit === 1) { - const msg = formDataImport.verificationphotohash.length > 0 ? 'Verication Photo Successfully Uploaded. Upload Another Photo to Change' : 'Upload Your Verification Photo to Mint Token'; - photoMsg = msg; - } - - if (photoLimit === 1 && claimPhoto) { - const msg = formDataImport.verificationphotohash.length > 0 ? 'Verication Photo Successfully Uploaded. Upload Another Photo to Change' : 'Upload Your Verification Photo to Claim Provenance'; - photoMsg = msg; - } - - if (photoLimit === 20) { - const msg = formDataImport && formDataImport.instrumentphotohashes.length >= 1 ? 'Please include all photos in your reupload' : 'Upload Your Item Photos'; - photoMsg = msg; - } - - return ( -

{photoMsg}

- ) - } - - {/*REMOVE WHEN DONE*/} - - function testdisplaymodal( ){ - setLoading(true) - uploadModalRef.current.style.display = 'block'; + if (photoLimit === 20) { + const msg = + formDataImport && formDataImport.instrumentphotohashes.length >= 1 + ? "Please include all photos in your reupload" + : "Upload Your Item Photos"; + photoMsg = msg; } - return ( - <> - -
- {unsupportedFiles.length === 0 && validFiles.length ? : ''} - {unsupportedFiles.length ?

Please remove all unsupported files.

: ''} - {itemPhotosUploaded ? - <> - {console.log(itemPhotosUploaded, "item phtos?")} - - - - - - : -
-
-
- {} -
- -
- } - - -
- { - validFiles.map((data, i) => -
-
openImageModal(data) : () => removeFile(data.name)}> -
-
{fileType(data.name)}
- {data.name} - ({fileSize(data.size)}) {data.invalid && ({errorMessage})} - - {itemPhotoLimitError && ({itemPhotoLimitError})} -
-
removeFile(data.name)}>X
-
- ) - } - {photoLimitMessage && ({photoLimitMessage})} -
+ return

{photoMsg}

; + }; + + { + /*REMOVE WHEN DONE*/ + } + + function testdisplaymodal() { + setLoading(true); + uploadModalRef.current.style.display = "block"; + } + + return ( + <> +
+ {unsupportedFiles.length === 0 && validFiles.length ? ( + + ) : ( + "" + )} + {unsupportedFiles.length ? ( +

Please remove all unsupported files.

+ ) : ( + "" + )} + {itemPhotosUploaded ? ( + <> + {console.log(itemPhotosUploaded, "item phtos?")} + + + + ) : ( +
+
+
+ {}
- -
-
- closeModal())}>X -
-
- -
-
-
closeUploadModal())}>X
-
- - {loading ?

Uploading Files...

: null } -
- {'please - -
- - -
+ +
+ )} + +
+ {validFiles.map((data, i) => ( +
+
openImageModal(data) + : () => removeFile(data.name) + } + > +
+
{fileType(data.name)}
+ + {data.name} + + ({fileSize(data.size)}){" "} + {data.invalid && ( + ({errorMessage}) + )} + {itemPhotoLimitError && ( + + ({itemPhotoLimitError}) + + )} +
+
removeFile(data.name)} + > + X +
- {/* */} - - ); -} + ))} + {photoLimitMessage && ( + ({photoLimitMessage}) + )} +
+
+ +
+
+ closeModal()}> + X + +
+
+ +
+
+
closeUploadModal()}> + X +
+
+ + {loading ?

Uploading Files...

: null} +
+ {"please +
+ + +
+
+ {/* */} + + ); +}; export default DragAndDrop; - - diff --git a/src/components/NavBar/NavBar.js b/src/components/NavBar/NavBar.js index 0a37e63..233c3e4 100644 --- a/src/components/NavBar/NavBar.js +++ b/src/components/NavBar/NavBar.js @@ -8,50 +8,53 @@ import { useUserContext } from "../../context/UserContext"; import { useTransferContext } from "../../context/TransferContext"; // styles imports -import { Container, Row, Col, Navbar, Nav, NavDropdown } from 'react-bootstrap/' -import styles from './NavBar.module.css' +import { + Container, + Row, + Col, + Navbar, + Nav, + NavDropdown, +} from "react-bootstrap/"; +import styles from "./NavBar.module.css"; import { truncateAddress } from "../../hooks/utils"; const NavBar = () => { const { mainAccount, setMainAccount } = useUserContext(); const { pendingTransferContracts } = useTransferContext(); - const [ quantity, setQuantity ] = useState(0); - - - useEffect(async() => { + const [quantity, setQuantity] = useState(0); + useEffect(async () => { if (pendingTransferContracts) { - loadPending(); - } - + loadPending(); + } + async function loadPending() { const transferQuantity = await pendingTransferContracts.length; - setQuantity(transferQuantity) - - } - - },[pendingTransferContracts]) - - return ( - <> - -
- - - - - - -
- -
- + setQuantity(transferQuantity); + } + }, [pendingTransferContracts]); + + return ( + <> +
+ + + + + +
+ +
+ - + - - -
- - - - ) - } - - export default NavBar; \ No newline at end of file + +
+
+ + ); +}; + +export default NavBar; diff --git a/src/components/NavBar/NavBar.module.css b/src/components/NavBar/NavBar.module.css index 373775b..e3996fb 100644 --- a/src/components/NavBar/NavBar.module.css +++ b/src/components/NavBar/NavBar.module.css @@ -1,35 +1,30 @@ .NavEdits { - align-items: center; - margin: 0 auto; - font-size: 20px; - font-weight: bold; + align-items: center; + margin: 0 auto; + font-size: 20px; + font-weight: bold; } +.titleAndLogo { + float: left; + font-size: 1.25rem; + white-space: nowrap; -.titleAndLogo{ - float: left; - font-size: 1.25rem; - white-space: nowrap; - - display: flex; - justify-content: center; - align-items: center; - + display: flex; + justify-content: center; + align-items: center; } .brandText { - font-size: 1.5rem; - font-weight: bold; - margin-right: 10px; - + font-size: 1.5rem; + font-weight: bold; + margin-right: 10px; } .incoming { - color: red; + color: red; } #incoming { - color:red; + color: red; } - - diff --git a/src/components/OfferModal/OfferModal.js b/src/components/OfferModal/OfferModal.js index b027b56..2ea2664 100644 --- a/src/components/OfferModal/OfferModal.js +++ b/src/components/OfferModal/OfferModal.js @@ -1,90 +1,99 @@ -import { Modal, Button} from 'react-bootstrap'; +import { Modal, Button } from "react-bootstrap"; -import { ethers } from 'ethers'; +import { ethers } from "ethers"; -import { useState } from 'react'; +import { useState } from "react"; //import './OfferModal.css'; +import { useUserContext } from "../../context/UserContext"; -import { useUserContext } from '../../context/UserContext'; - -import styles from './OfferModal.module.css'; +import styles from "./OfferModal.module.css"; export function OfferModal({ - handleChange, + handleChange, handleClose, - newOfferAmount, - show, + newOfferAmount, + show, provenanceContract, - setOfferMade}) { - - - - const { provider } = useUserContext(); - - const [ successMessage, setSuccessMessage] = useState(''); - - - async function offer() { - await provenanceContract.makeOffer({value: ethers.utils.parseEther(newOfferAmount)}) - .then(async(result) => { - - - provider.waitForTransaction(result.hash) - .then(mined => { - if (mined) { - provenanceContract.once("OfferMade", (buyer, amount) => { - setOfferMade(true) - setSuccessMessage("Offer Successful!") - setTimeout(handleClose, 4000) - }) - }}) + setOfferMade, +}) { + const { provider } = useUserContext(); + + const [successMessage, setSuccessMessage] = useState(""); + + async function offer() { + await provenanceContract + .makeOffer({ value: ethers.utils.parseEther(newOfferAmount) }) + .then(async (result) => { + provider.waitForTransaction(result.hash).then((mined) => { + if (mined) { + provenanceContract.once("OfferMade", (buyer, amount) => { + setOfferMade(true); + setSuccessMessage("Offer Successful!"); + setTimeout(handleClose, 4000); + }); + } + }); }) - .catch(error => console.log(error.data.message, "offer error")); - } - - + .catch((error) => console.log(error.data.message, "offer error")); + } - - return ( - - {successMessage ? + return ( + + {successMessage ? (

{successMessage}

- : + ) : ( <> - - Make an offer for this Provenance + + Make an offer for this Provenance - - {newOfferAmount > 0 ? -

You are offering {ethers.constants.EtherSymbol}{newOfferAmount} for this provenance

- : null} - + {newOfferAmount > 0 ? ( +

+ You are offering {ethers.constants.EtherSymbol} + {newOfferAmount} for this provenance +

+ ) : null} + + value={newOfferAmount || ""} + style={{ + width: "65%", + height: "40px", + marginBottom: "20px", + fontSize: "20px", + textAlign: "center", + }} + />
- -
+ +
- - } - - ); - } - - \ No newline at end of file + )} + + ); +} diff --git a/src/components/OfferModal/OfferModal.module.css b/src/components/OfferModal/OfferModal.module.css index c21422d..5b71ccf 100644 --- a/src/components/OfferModal/OfferModal.module.css +++ b/src/components/OfferModal/OfferModal.module.css @@ -1,6 +1,6 @@ .successMessage { - text-align: center; - color:green; - font-size: 30px; - margin: 20px; -} \ No newline at end of file + text-align: center; + color: green; + font-size: 30px; + margin: 20px; +} diff --git a/src/components/Pagination/Pagination.js b/src/components/Pagination/Pagination.js index 01d1fab..b60ebf0 100644 --- a/src/components/Pagination/Pagination.js +++ b/src/components/Pagination/Pagination.js @@ -1,10 +1,10 @@ //- React/NextJS Imports -import React from 'react'; -import Link from 'next/link'; -import { useRouter } from 'next/router'; +import React from "react"; +import Link from "next/link"; +import { useRouter } from "next/router"; //- Style Imports -import styles from './Pagination.module.css'; +import styles from "./Pagination.module.css"; const Pagination = ({ postsPerPage, totalPosts, paginate, slug }) => { const pageNumbers = []; @@ -15,32 +15,34 @@ const Pagination = ({ postsPerPage, totalPosts, paginate, slug }) => { const router = useRouter(); - const page = Number(router.query?.page) ? Number(router.query?.page) : 1; + const page = Number(router.query?.page) ? Number(router.query?.page) : 1; - const button = (p) => { - return p === page ? ( -
  • - {p} -
  • - ) : ( -
  • - - paginate(p)}>{p} - -
  • - ); - }; + const button = (p) => { + return p === page ? ( +
  • + {p} +
  • + ) : ( +
  • + + paginate(p)}> + {p} + + +
  • + ); + }; return ( ); }; -export default Pagination; \ No newline at end of file +export default Pagination; diff --git a/src/components/Pagination/Pagination.module.css b/src/components/Pagination/Pagination.module.css index 728928e..64ca911 100644 --- a/src/components/Pagination/Pagination.module.css +++ b/src/components/Pagination/Pagination.module.css @@ -1,55 +1,54 @@ - .Pages { - display: flex; - padding: 0; - list-style: none; - justify-content: center; + display: flex; + padding: 0; + list-style: none; + justify-content: center; } .Pages li { - height: 32px; - width: 32px; - line-height: 32px; - background: var(--text-primary); - color: var(--color-black); - text-align: center; + height: 32px; + width: 32px; + line-height: 32px; + background: var(--text-primary); + color: var(--color-black); + text-align: center; - border-radius: 4px; + border-radius: 4px; } .Pages li a { - display: block; - height: 100%; - width: 100%; + display: block; + height: 100%; + width: 100%; } .Pages li a:hover { - color: var(--color-black); + color: var(--color-black); } .Pages li:not(:first-of-type) { - margin-left: 8px; + margin-left: 8px; } .Pages li:hover { - background: var(--color-primary); + background: var(--color-primary); } .Selected { - background: var(--color-primary) !important; + background: var(--color-primary) !important; } .Disabled { - cursor: default; - background: var(--color-grey) !important; + cursor: default; + background: var(--color-grey) !important; } .Pages li:active { - background: none !important; - color: var(--color-primary) !important; - border: 1px solid var(--color-primary) !important; + background: none !important; + color: var(--color-primary) !important; + border: 1px solid var(--color-primary) !important; } .Pages li:active a { - color: var(--color-primary) !important; -} \ No newline at end of file + color: var(--color-primary) !important; +} diff --git a/src/components/Pagination/UpdatesPagination.module.css b/src/components/Pagination/UpdatesPagination.module.css index d02a142..64ca911 100644 --- a/src/components/Pagination/UpdatesPagination.module.css +++ b/src/components/Pagination/UpdatesPagination.module.css @@ -1,54 +1,54 @@ .Pages { - display: flex; - padding: 0; - list-style: none; - justify-content: center; + display: flex; + padding: 0; + list-style: none; + justify-content: center; } .Pages li { - height: 32px; - width: 32px; - line-height: 32px; - background: var(--text-primary); - color: var(--color-black); - text-align: center; + height: 32px; + width: 32px; + line-height: 32px; + background: var(--text-primary); + color: var(--color-black); + text-align: center; - border-radius: 4px; + border-radius: 4px; } .Pages li a { - display: block; - height: 100%; - width: 100%; + display: block; + height: 100%; + width: 100%; } .Pages li a:hover { - color: var(--color-black); + color: var(--color-black); } .Pages li:not(:first-of-type) { - margin-left: 8px; + margin-left: 8px; } .Pages li:hover { - background: var(--color-primary); + background: var(--color-primary); } .Selected { - background: var(--color-primary) !important; + background: var(--color-primary) !important; } .Disabled { - cursor: default; - background: var(--color-grey) !important; + cursor: default; + background: var(--color-grey) !important; } .Pages li:active { - background: none !important; - color: var(--color-primary) !important; - border: 1px solid var(--color-primary) !important; + background: none !important; + color: var(--color-primary) !important; + border: 1px solid var(--color-primary) !important; } .Pages li:active a { - color: var(--color-primary) !important; + color: var(--color-primary) !important; } diff --git a/src/components/Pagination/UpdatesPagination.tsx b/src/components/Pagination/UpdatesPagination.tsx index 5e35051..fa4d023 100644 --- a/src/components/Pagination/UpdatesPagination.tsx +++ b/src/components/Pagination/UpdatesPagination.tsx @@ -1,10 +1,10 @@ //- React/NextJS Imports -import React from 'react'; -import Link from 'next/link'; -import { useRouter } from 'next/router'; +import React from "react"; +import Link from "next/link"; +import { useRouter } from "next/router"; //- Style Imports -import styles from './UpdatesPagination.module.css'; +import styles from "./UpdatesPagination.module.css"; type Props = { pages: number; @@ -21,32 +21,36 @@ const Pagination = ({ postsPerPage, totalPosts, paginate }) => { const router = useRouter(); - const page = Number(router.query?.page) ? Number(router.query?.page) : 1; + const page = Number(router.query?.page) ? Number(router.query?.page) : 1; - const button = (p: number) => { - return p === page ? ( -
  • - {p} -
  • - ) : ( -
  • - - paginate(p)}>{p} - -
  • - ); - }; + const button = (p: number) => { + return p === page ? ( +
  • + {p} +
  • + ) : ( +
  • + + paginate(p)}> + {p} + + +
  • + ); + }; return ( ); }; -export default Pagination; \ No newline at end of file +export default Pagination; diff --git a/src/components/PhotoPreviews.js b/src/components/PhotoPreviews.js index a37bf6b..ddce4e9 100644 --- a/src/components/PhotoPreviews.js +++ b/src/components/PhotoPreviews.js @@ -1,27 +1,24 @@ - // style imports -import styles from './PhotoPreviews.module.css'; +import styles from "./PhotoPreviews.module.css"; import { Carousel, Spinner } from "react-bootstrap"; import { useEffect, useState } from "react"; const PhotoPreviews = ({ - formData, - photoLimit, - readyToMint, - unusedTokenID, - tokenToMint, - ipfsGetterRootURL, - claimPhoto}) => { - - - - const [ itemPhotoArray, setItemPhotoArray ] = useState([]); -; - // let itemPhotoArray = []; - let verificationHashURL = "https://gateway.pinata.cloud/ipfs/" + formData.verificationphotohash; - - // maybe should do useEffect here - /* + formData, + photoLimit, + readyToMint, + unusedTokenID, + tokenToMint, + ipfsGetterRootURL, + claimPhoto, +}) => { + const [itemPhotoArray, setItemPhotoArray] = useState([]); + // let itemPhotoArray = []; + let verificationHashURL = + "https://gateway.pinata.cloud/ipfs/" + formData.verificationphotohash; + + // maybe should do useEffect here + /* for (let ipfsHash of formData.instrumentphotohashes) { @@ -31,102 +28,81 @@ const PhotoPreviews = ({ } */ -// console.log(formData.instrumentphotohashes) - - useEffect(async() => { - - if (formData.instrumentphotohashes) { - loadItemHashes() - } - - async function loadItemHashes() { + // console.log(formData.instrumentphotohashes) - let itemArray = []; + useEffect(async () => { + if (formData.instrumentphotohashes) { + loadItemHashes(); + } - for (let i = 0; i < (await formData.instrumentphotohashes).length; i++) { - - let itemPhotosHashURL = ipfsGetterRootURL + (formData.instrumentphotohashes)[i]; - itemArray.push(itemPhotosHashURL); - } - setItemPhotoArray(itemArray); - + async function loadItemHashes() { + let itemArray = []; - - + for (let i = 0; i < (await formData.instrumentphotohashes).length; i++) { + let itemPhotosHashURL = + ipfsGetterRootURL + formData.instrumentphotohashes[i]; + itemArray.push(itemPhotosHashURL); } - - },[formData.instrumentphotohashes, ipfsGetterRootURL]) - - - const ItemPhotoCarousel = () => { - - return ( -
    - - {itemPhotoArray.length > 0 ? - - {itemPhotoArray.map((photo, index)=> { - return ( - - item photos not yet loaded - - ) - })} - - - : -
    -

    Item Photos Loading

    - -
    - } - -
    - ) + setItemPhotoArray(itemArray); } - - - - - if (formData.verificationphotohash.length > 0 || itemPhotoArray.length >= 1 ) { - return ( - -
    - - {/* need to fix this flow and correct token #s */} - { photoLimit == 1 ? - -
    - { claimPhoto ? -

    New Verification Photo for Provenance

    : - readyToMint ? -

    Verification Photo for Token {tokenToMint}

    : - formData.verificationphotohash.length == 0 - ? null :

    Verification Photo for Token {unusedTokenID}

    - } - {formData.verificationphotohash.length == 0 ? - null : - verification photo not yet uploaded - } -
    - - : - - } - - - + }, [formData.instrumentphotohashes, ipfsGetterRootURL]); + + const ItemPhotoCarousel = () => { + return ( +
    + {itemPhotoArray.length > 0 ? ( + + {itemPhotoArray.map((photo, index) => { + return ( + + item photos not yet loaded + + ); + })} + + ) : ( +
    +

    Item Photos Loading

    + +
    + )}
    - - )} else { - return null - } + ); + }; + + if (formData.verificationphotohash.length > 0 || itemPhotoArray.length >= 1) { + return ( +
    + {/* need to fix this flow and correct token #s */} + {photoLimit == 1 ? ( +
    + {claimPhoto ? ( +

    New Verification Photo for Provenance

    + ) : readyToMint ? ( +

    Verification Photo for Token {tokenToMint}

    + ) : formData.verificationphotohash.length == 0 ? null : ( +

    Verification Photo for Token {unusedTokenID}

    + )} + {formData.verificationphotohash.length == 0 ? null : ( + verification photo not yet uploaded + )} +
    + ) : ( + + )} +
    + ); + } else { + return null; } +}; - export default PhotoPreviews; - - \ No newline at end of file +export default PhotoPreviews; diff --git a/src/components/PhotoPreviews.module.css b/src/components/PhotoPreviews.module.css index 4ab7780..ee15755 100644 --- a/src/components/PhotoPreviews.module.css +++ b/src/components/PhotoPreviews.module.css @@ -1,4 +1,3 @@ .itemPhotoCarousel { - height: 300px; + height: 300px; } - diff --git a/src/components/ProvenanceHistory/ProvenanceHistory.js b/src/components/ProvenanceHistory/ProvenanceHistory.js index 8a4267b..a30bd82 100644 --- a/src/components/ProvenanceHistory/ProvenanceHistory.js +++ b/src/components/ProvenanceHistory/ProvenanceHistory.js @@ -1,91 +1,84 @@ -import { useEffect, useState } from 'react'; - +import { useEffect, useState } from "react"; //react-bootstrap imports -import { Container, Card, Image } from 'react-bootstrap'; +import { Container, Card, Image } from "react-bootstrap"; // context imports -import { useItemContext } from '../../../src/context/ItemContext'; -import { useContractContext } from '../../../src/context/ContractContext'; +import { useItemContext } from "../../../src/context/ItemContext"; +import { useContractContext } from "../../../src/context/ContractContext"; // styles -import styles from './ProvenanceHistory.module.css' - -const ProvenanceHistory = ({provenanceContract, loaded}) => { - - const [provenanceHistoryArray, setProvenanceHistoryArray] = useState([]); - - const { MothershipContract } = useContractContext(); +import styles from "./ProvenanceHistory.module.css"; - const { ipfsGetterRootURL, bytes32ToString } = useItemContext(); +const ProvenanceHistory = ({ provenanceContract, loaded }) => { + const [provenanceHistoryArray, setProvenanceHistoryArray] = useState([]); - useEffect(async() => { - - if (MothershipContract && loaded) { - loadProvenanceHistory() - } - async function loadProvenanceHistory() { - const history = await provenanceContract.getOwnershipHistory(); - const historyShift = [...history]; - historyShift.shift(); - // The line below is throwing an error due to state update, memory leak. make a note to fix. - setProvenanceHistoryArray(historyShift); - - } - },[MothershipContract, loaded]) + const { MothershipContract } = useContractContext(); + const { ipfsGetterRootURL, bytes32ToString } = useItemContext(); - if (loaded){ - return ( -
    -
    -

    Provenance Ownership History

    - {provenanceHistoryArray.map((provenanceOwnerInfo, index)=> { - const { ownerAddress, date, verificationPhotoHash, ownerCount } = provenanceOwnerInfo; - - const dateFormatted = bytes32ToString(date); - return ( + useEffect(async () => { + if (MothershipContract && loaded) { + loadProvenanceHistory(); + } + async function loadProvenanceHistory() { + const history = await provenanceContract.getOwnershipHistory(); + const historyShift = [...history]; + historyShift.shift(); + // The line below is throwing an error due to state update, memory leak. make a note to fix. + setProvenanceHistoryArray(historyShift); + } + }, [MothershipContract, loaded]); - - - - Owner {ownerCount} - -
    -
    - {'verification -
    -
    - Owner Address - - {ownerAddress} - - Date Acquired - + - {dateFormatted} - + if (loaded) { + return ( +
    +
    +

    Provenance Ownership History

    + {provenanceHistoryArray.map((provenanceOwnerInfo, index) => { + const { ownerAddress, date, verificationPhotoHash, ownerCount } = + provenanceOwnerInfo; - Notes - - Additional data can maybe stored off-chain? - -
    - - -
    + const dateFormatted = bytes32ToString(date); + return ( + + + Owner {ownerCount} + +
    +
    + {"verification +
    +
    + Owner Address + + {ownerAddress} + + Date Acquired + + +{dateFormatted} + - - - - ) - }) - } -
    - - )} else { - return ( -

    nothing to show here

    - )} -} + Notes + + Additional data can maybe stored off-chain? + +
    +
    +
    +
    +
    + ); + })} +
    + ); + } else { + return

    nothing to show here

    ; + } +}; -export default ProvenanceHistory; \ No newline at end of file +export default ProvenanceHistory; diff --git a/src/components/ProvenanceHistory/ProvenanceHistory.module.css b/src/components/ProvenanceHistory/ProvenanceHistory.module.css index 02a84aa..3b916d5 100644 --- a/src/components/ProvenanceHistory/ProvenanceHistory.module.css +++ b/src/components/ProvenanceHistory/ProvenanceHistory.module.css @@ -1,26 +1,23 @@ .itemTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .tokenTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .container { - text-align: center; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; - + text-align: center; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } /* .ownedContainer { @@ -56,33 +53,27 @@ } */ - - - - .cardContainer { - display: flex; - height: 250px; + display: flex; + height: 250px; } .cardPhotoContainer { - flex: 1; - max-width: 25%; - object-fit: contain; + flex: 1; + max-width: 25%; + object-fit: contain; } .cardPhoto { - max-width: 100%; - max-height: 100%; - + max-width: 100%; + max-height: 100%; } .cardInfoContainer { - flex: 2; - border: solid 2px lightgray; - border-radius: 10px; + flex: 2; + border: solid 2px lightgray; + border-radius: 10px; - padding-top: 5px; - margin-left: 10px; - -} \ No newline at end of file + padding-top: 5px; + margin-left: 10px; +} diff --git a/src/components/PublicItemTable/PublicItemTable.js b/src/components/PublicItemTable/PublicItemTable.js index 359ce6b..85209ed 100644 --- a/src/components/PublicItemTable/PublicItemTable.js +++ b/src/components/PublicItemTable/PublicItemTable.js @@ -1,37 +1,41 @@ -import { useEffect, useState } from 'react'; +import { useEffect, useState } from "react"; //ethers import -import { ethers } from 'ethers'; -import { formatEther } from 'ethers/lib/utils'; +import { ethers } from "ethers"; +import { formatEther } from "ethers/lib/utils"; // Next Imports -import Link from 'next/link'; -import Image from 'next/image'; -import { useRouter } from 'next/router'; +import Link from "next/link"; +import Image from "next/image"; +import { useRouter } from "next/router"; //context imports -import { useItemContext } from '../../context/ItemContext'; -import { useUserContext } from '../../context/UserContext'; +import { useItemContext } from "../../context/ItemContext"; +import { useUserContext } from "../../context/UserContext"; // components Imports // import Pagination from '../Pagination/Pagination'; //style imports -import styles from './PublicItemTable.module.css'; -import { Container, Table, Row, Col, Card, Spinner, NavbarBrand } from 'react-bootstrap'; - - +import styles from "./PublicItemTable.module.css"; +import { + Container, + Table, + Row, + Col, + Card, + Spinner, + NavbarBrand, +} from "react-bootstrap"; const PublicItemTable = ({ provenanceObjects }) => { - const { mainAccount, signer } = useUserContext() - const {ipfsGetterRootURL, bytes32ToString } = useItemContext(); + const { mainAccount, signer } = useUserContext(); + const { ipfsGetterRootURL, bytes32ToString } = useItemContext(); // const [currentPage, setCurrentPage] = useState(1); // const [currentProvenances, setCurrentProvenances] = useState(); // const [postsPerPage] = useState(10); - - // useEffect(()=> { // if (provenanceObjects){ @@ -42,123 +46,188 @@ const PublicItemTable = ({ provenanceObjects }) => { // },[provenanceObjects]) - // // set number of pages for interviews - - // const paginate = pageNum => setCurrentPage(pageNum); - + // // set number of pages for interviews + // const paginate = pageNum => setCurrentPage(pageNum); const router = useRouter(); const ProvenanceTable = () => { - - if (provenanceObjects && provenanceObjects.length > 0){ + if (provenanceObjects && provenanceObjects.length > 0) { return ( - - {provenanceObjects.map((array, index) => { - - const { ProvenanceContract, ProvenanceProps, ProvenanceOwnerInfo, ProvenancePendingOwner, ProvenanceCurrentOffer } = array; - const provenanceAddress = ProvenanceContract.address; - - const{ serial, brand, instrumentDeedToken, model, year, typeOfProvenance } = ProvenanceProps; - - const { ownerAddress, name, verificationPhotoHash, date} = ProvenanceOwnerInfo; - const { buyer, offer } = ProvenanceCurrentOffer; - - const currentOffer = formatEther(offer.toString()); - - - const pendingBool = !(ProvenancePendingOwner === ethers.constants.AddressZero); - - const ownerBool = (ownerAddress === mainAccount); - - - - return ( - - -
    - {/* */} - - {/* not a complete fix, need to deactivate transfer functions on direct provenance url as well, not just deactivate link */} - - - - 0 && !ownerBool) ? styles.offerBorder : null} - className={ownerBool ? - styles.ownedContainer : - pendingBool ? styles.pendingContainer : styles.publicContainer}> + {provenanceObjects.map((array, index) => { + const { + ProvenanceContract, + ProvenanceProps, + ProvenanceOwnerInfo, + ProvenancePendingOwner, + ProvenanceCurrentOffer, + } = array; + const provenanceAddress = ProvenanceContract.address; + + const { + serial, + brand, + instrumentDeedToken, + model, + year, + typeOfProvenance, + } = ProvenanceProps; + + const { ownerAddress, name, verificationPhotoHash, date } = + ProvenanceOwnerInfo; + const { buyer, offer } = ProvenanceCurrentOffer; + + const currentOffer = formatEther(offer.toString()); + + const pendingBool = !( + ProvenancePendingOwner === ethers.constants.AddressZero + ); + + const ownerBool = ownerAddress === mainAccount; + + return ( +
    + {/* */} + + {/* not a complete fix, need to deactivate transfer functions on direct provenance url as well, not just deactivate link */} + + 0 && !ownerBool + ? styles.offerBorder + : null + } + className={ + ownerBool + ? styles.ownedContainer + : pendingBool + ? styles.pendingContainer + : styles.publicContainer + } + > {/* fix below h2 */} -

    {brand} {model}

    - {(pendingBool && ownerBool) ?

    This provenance has been released and is awaiting claim by buyer {ProvenancePendingOwner}

    : null } - {(pendingBool && !ownerBool) ? Claim This Provenance : null } - {currentOffer > 0 ?
    CURRENT OFFER ON THIS PROVENANCE: {ethers.constants.EtherSymbol}{currentOffer}
    : null} - - - - - - - - - - - - - {ownerBool ? - null - : - - - - } - - - - - - - - - - - +

    + {brand} {model} +

    + {pendingBool && ownerBool ? ( +

    + This provenance has been released and is awaiting + claim by buyer {ProvenancePendingOwner} +

    + ) : null} + {pendingBool && !ownerBool ? ( + + Claim This Provenance + + ) : null} + {currentOffer > 0 ? ( +
    + CURRENT OFFER ON THIS PROVENANCE:{" "} + {ethers.constants.EtherSymbol} + {currentOffer} +
    + ) : null} + + + + + + + + +
    Owner Address: {ownerAddress}
    Provenance Address: {provenanceAddress}
    Brand: {brand}
    Model: {model}
    Token ID: {instrumentDeedToken.toString()}
    + + {ownerBool ? null : ( + + - - - - - - - - -
    Owner Address: {ownerAddress}
    Type of Item: {typeOfProvenance.toString()}
    Year: {year}
    - - -
    -
    + )} + + + Provenance Address: {provenanceAddress} + + + + Brand: {brand} + + + Model: {model} + + + + Token ID: {instrumentDeedToken.toString()} + + + + + Type of Item:{" "} + {typeOfProvenance.toString()} + + + + Year: {year} + + + + + +
    - - + - {/*
    */} -
    - ) - }) - } - - + {/*
    */} +
    + ); + })}
    {/* { paginate={paginate} /> */}
    - )} else { - return ( -
    - {(mainAccount) ? - <> -

    No Provenances Returned.

    - - : - <> -

    You have no registered Provenances.

    - - } -
    - - - )} -} - - - - return( -
    - {(mainAccount) ? - null - :

    Registered Provenances for
    {mainAccount}

    - } - {/**/} - - - + ); + } else { + return ( +
    + {mainAccount ? ( + <> +

    No Provenances Returned.

    + + ) : ( + <> +

    + You have no registered Provenances. +

    + + )}
    - ) -} - + ); + } + }; + + return ( +
    + {mainAccount ? null : ( +

    + Registered Provenances for
    {mainAccount} +

    + )} + {/**/} + +
    + ); +}; export default PublicItemTable; - - - diff --git a/src/components/PublicItemTable/PublicItemTable.module.css b/src/components/PublicItemTable/PublicItemTable.module.css index c11051a..6b14295 100644 --- a/src/components/PublicItemTable/PublicItemTable.module.css +++ b/src/components/PublicItemTable/PublicItemTable.module.css @@ -1,104 +1,100 @@ .itemTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .tokenTable { - border-collapse: collapse; - border: 1px solid black; - margin: 0 auto; - margin-bottom: 40px; - + border-collapse: collapse; + border: 1px solid black; + margin: 0 auto; + margin-bottom: 40px; } .container { - text-align: center; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; - + text-align: center; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } .row { - display: flex; - flex-wrap: wrap - } + display: flex; + flex-wrap: wrap; +} .ownedContainer { - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; - border: 6px solid lightgreen; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; + border: 6px solid lightgreen; } .ownedContainer:hover { - background-color: azure; + background-color: azure; } .pendingContainer { - text-align: center; - border: 6px solid red; - border-radius: 25px; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; + text-align: center; + border: 6px solid red; + border-radius: 25px; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } .publicContainer { - border: 2px solid lightgray; - border-radius: 25px; - width: 75%; - margin: 0 auto; - margin-top: 10px; - padding-top: 10px; + border: 2px solid lightgray; + border-radius: 25px; + width: 75%; + margin: 0 auto; + margin-top: 10px; + padding-top: 10px; } #offerBorder { - border: 6px solid orange; + border: 6px solid orange; } .linkPlacebo { - color: #0d6efd; - text-decoration: underline; + color: #0d6efd; + text-decoration: underline; } .itemPhotoCarousel { - flex: 1; - object-fit: cover; - height: 300px; - + flex: 1; + object-fit: cover; + height: 300px; } /* Image Dimensions */ .imageContainer { - width: 100%; - height: 100%; + width: 100%; + height: 100%; + position: relative !important; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: center; + min-width: 250px; +} + +.image { + border-radius: 10px; +} + +@media (max-width: 1313px) { + .imageContainer { position: relative !important; margin: 0 auto; display: flex; align-items: center; justify-content: center; - min-width: 250px; - } - .image { - border-radius: 10px; + height: 100%; + width: 50%; } - - @media (max-width: 1313px) { - .imageContainer { - position: relative !important; - margin: 0 auto; - display: flex; - align-items: center; - justify-content: center; - - height: 100%; - width: 50%; - } - } \ No newline at end of file +} diff --git a/src/components/web3Modal/providerOptions.js b/src/components/web3Modal/providerOptions.js index fbc4a1d..8813d04 100644 --- a/src/components/web3Modal/providerOptions.js +++ b/src/components/web3Modal/providerOptions.js @@ -8,22 +8,22 @@ export const providerOptions = { package: CoinbaseWalletSDK, // Required options: { appName: "Web 3 Modal Demo", // Required - infuraId:process.env.NEXT_PUBLIC_RINKEBY_ENDPOINT // Required unless you provide a JSON RPC url; see `rpc` below - } + infuraId: process.env.NEXT_PUBLIC_RINKEBY_ENDPOINT, // Required unless you provide a JSON RPC url; see `rpc` below + }, }, walletconnect: { package: WalletConnect, // required options: { - infuraId: process.env.NEXT_PUBLIC_RINKEBY_ENDPOINT // required - } + infuraId: process.env.NEXT_PUBLIC_RINKEBY_ENDPOINT, // required + }, }, -// https://github.com/Web3Modal/web3modal/blob/master/docs/providers/portis.md + // https://github.com/Web3Modal/web3modal/blob/master/docs/providers/portis.md portis: { package: Portis, // required options: { - id: "PORTIS_ID_PLACEHOLDER_NEED REAL ONE" // required - } - } + id: "PORTIS_ID_PLACEHOLDER_NEED REAL ONE", // required + }, + }, /* torus: { diff --git a/src/context/ContractContext.js b/src/context/ContractContext.js index bddb61c..aa8d893 100644 --- a/src/context/ContractContext.js +++ b/src/context/ContractContext.js @@ -1,11 +1,10 @@ // src/context/state.js -import { createContext, useContext } from 'react'; +import { createContext, useContext } from "react"; // import useContractObjectRepo from '../hooks/useContractRepo'; import { useState, useEffect } from "react"; -import { ethers } from 'ethers'; - +import { ethers } from "ethers"; //context @@ -13,57 +12,54 @@ import { useUserContext } from "../context/UserContext"; //abis -import Mothership from '../../artifacts/contracts/Mothership.sol/Mothership.json'; -import InstrumentDeedToken from '../../artifacts/contracts/InstrumentDeedToken.sol/InstrumentDeedToken.json'; +import Mothership from "../../artifacts/contracts/Mothership.sol/Mothership.json"; +import InstrumentDeedToken from "../../artifacts/contracts/InstrumentDeedToken.sol/InstrumentDeedToken.json"; //addresses -import deployedTokenAddress from '../deployedContractAddresses/instrumenttokenaddress.json'; -import deployedMothershipAddress from '../deployedContractAddresses/mothershipaddress.json'; +import deployedTokenAddress from "../deployedContractAddresses/instrumenttokenaddress.json"; +import deployedMothershipAddress from "../deployedContractAddresses/mothershipaddress.json"; -const ContractContext= createContext(); +const ContractContext = createContext(); export function ContractContextProvider({ children }) { - -// const { MothershipContract, TokenContract } = useContractObjectRepo() - -const { provider, modalProvider, signer } = useUserContext(); -// console.log(signer, "top signer check") - -const [MothershipContract, setMothershipContract] = useState(''); -const [TokenContract, setTokenContract] = useState(''); - - - -//need to adjust this hook for non-Ethereum - useEffect(async()=> { - - - contractObjects(); - - async function contractObjects() { - - - if (signer) { - - //MothershipContract - const MothershipContractAddress = deployedMothershipAddress.address; - const MothershipContractObject = new ethers.Contract(MothershipContractAddress, Mothership.abi, signer); - setMothershipContract(MothershipContractObject); - - //TokenContract (need to put this in here Mothership and Provenance) - const TokenContractAddress = deployedTokenAddress.address; - const TokenContractObject = new ethers.Contract(TokenContractAddress, InstrumentDeedToken.abi, signer); - setTokenContract(TokenContractObject); - } - - } - - },[signer]) + // const { MothershipContract, TokenContract } = useContractObjectRepo() + + const { provider, modalProvider, signer } = useUserContext(); + // console.log(signer, "top signer check") + + const [MothershipContract, setMothershipContract] = useState(""); + const [TokenContract, setTokenContract] = useState(""); + + //need to adjust this hook for non-Ethereum + useEffect(async () => { + contractObjects(); + + async function contractObjects() { + if (signer) { + //MothershipContract + const MothershipContractAddress = deployedMothershipAddress.address; + const MothershipContractObject = new ethers.Contract( + MothershipContractAddress, + Mothership.abi, + signer + ); + setMothershipContract(MothershipContractObject); + + //TokenContract (need to put this in here Mothership and Provenance) + const TokenContractAddress = deployedTokenAddress.address; + const TokenContractObject = new ethers.Contract( + TokenContractAddress, + InstrumentDeedToken.abi, + signer + ); + setTokenContract(TokenContractObject); + } + } + }, [signer]); const state = { MothershipContract, TokenContract }; - return ( {children} @@ -73,4 +69,4 @@ const [TokenContract, setTokenContract] = useState(''); export function useContractContext() { return useContext(ContractContext); -} \ No newline at end of file +} diff --git a/src/context/ItemContext.js b/src/context/ItemContext.js index f0758cc..04744e0 100644 --- a/src/context/ItemContext.js +++ b/src/context/ItemContext.js @@ -1,224 +1,215 @@ // src/context/state.js -import { createContext, useContext, useState, useEffect } from 'react'; +import { createContext, useContext, useState, useEffect } from "react"; //ethers -import { ethers } from 'ethers'; +import { ethers } from "ethers"; -import bs58 from 'bs58'; +import bs58 from "bs58"; //abi -import Provenance from '../../artifacts/contracts/Provenance.sol/Provenance.json'; +import Provenance from "../../artifacts/contracts/Provenance.sol/Provenance.json"; //context imports -import { useUserContext } from './UserContext.js'; -import { useContractContext} from './ContractContext.js' +import { useUserContext } from "./UserContext.js"; +import { useContractContext } from "./ContractContext.js"; - -const ItemContext= createContext(); +const ItemContext = createContext(); export function ItemContextProvider({ children }) { // use hooks to handle these - const { mainAccount, signer } = useUserContext(); + const { mainAccount, signer } = useUserContext(); const { MothershipContract, TokenContract } = useContractContext(); - // + // - const [ itemAdded, setItemAdded ] = useState(false); - const [ newProvenanceAddress, setNewProvenanceAddress] = useState(''); - const [ tokens, setTokens ] = useState([]); - const [ items, setItems ] = useState([]); - const [ provenanceObjects, setProvenanceObjects] = useState([]); + const [itemAdded, setItemAdded] = useState(false); + const [newProvenanceAddress, setNewProvenanceAddress] = useState(""); + const [tokens, setTokens] = useState([]); + const [items, setItems] = useState([]); + const [provenanceObjects, setProvenanceObjects] = useState([]); const ipfsGetterRootURL = "https://gateway.pinata.cloud/ipfs/"; - -//load user tokens. need the for loop to convert to #. - useEffect(() => { - if (mainAccount) { - getTokens() - .then(tokenIDs => { + //load user tokens. need the for loop to convert to #. + useEffect(() => { + if (mainAccount) { + getTokens().then((tokenIDs) => { let tokenArray = []; if (tokenIDs) { - for (let i = 0; i < tokenIDs.length; i++) { - const tokenIndex = tokenIDs[i].toNumber(); - tokenArray.push(tokenIndex) - }} - setTokens(tokenArray) - }) - } - - async function getTokens() { - - if (TokenContract && mainAccount) { - let tokenIDs = await TokenContract.tokensOfOwner(mainAccount); - //tokenArray.push(tokenIDs) - return tokenIDs + for (let i = 0; i < tokenIDs.length; i++) { + const tokenIndex = tokenIDs[i].toNumber(); + tokenArray.push(tokenIndex); + } } - - }; - - }, [mainAccount, TokenContract]) - - - //load addresses of user provenances - useEffect(() => { - if (mainAccount && MothershipContract) { - // console.log(mainAccount, "mainAccount in getItems UseEffect") - getItems(); - } - - async function getItems() { - if (MothershipContract) { + setTokens(tokenArray); + }); + } + + async function getTokens() { + if (TokenContract && mainAccount) { + let tokenIDs = await TokenContract.tokensOfOwner(mainAccount); + //tokenArray.push(tokenIDs) + return tokenIDs; + } + } + }, [mainAccount, TokenContract]); + + //load addresses of user provenances + useEffect(() => { + if (mainAccount && MothershipContract) { + // console.log(mainAccount, "mainAccount in getItems UseEffect") + getItems(); + } + + async function getItems() { + if (MothershipContract) { let contractItems = await MothershipContract.getOwnersInstruments(); setItems(contractItems); - } - }; - - },[mainAccount, MothershipContract]) - - //Loads all users provenance contract instances - - useEffect(() => { - if (items || newProvenanceAddress || itemAdded || mainAccount) { - // console.log(mainAccount, "mainAccount in items UE") - populateProvenances() + } + } + }, [mainAccount, MothershipContract]); + + //Loads all users provenance contract instances + + useEffect(() => { + if (items || newProvenanceAddress || itemAdded || mainAccount) { + // console.log(mainAccount, "mainAccount in items UE") + populateProvenances() /* .then(setItemAdded(false)) */ - .catch(error => console.log(error, 'populate error')); + .catch((error) => console.log(error, "populate error")); + } + async function populateProvenances() { + let provenanceArray = []; + + for (let address of items) { + const ProvenanceContract = new ethers.Contract( + address, + Provenance.abi, + signer + ); + + const ProvenanceDetails = await ProvenanceContract.instrument(); + const itemPhotos = await ProvenanceContract.getItemPics(); + const ProvenanceProps = { ...ProvenanceDetails, itemPhotos }; + + const index = ProvenanceContract.ownerCount(); + const ProvenanceOwnerInfo = await ProvenanceContract.ownerProvenance( + index + ); + + const ProvenancePendingOwner = await ProvenanceContract.pendingOwner(); + + const ProvenanceCurrentOffer = await ProvenanceContract.currentOffer(); + + provenanceArray.push({ + ProvenanceContract: ProvenanceContract, + ProvenanceProps: ProvenanceProps, + ProvenanceOwnerInfo: ProvenanceOwnerInfo, + ProvenancePendingOwner: ProvenancePendingOwner, + ProvenanceCurrentOffer: ProvenanceCurrentOffer, + }); } - async function populateProvenances() { - let provenanceArray = []; - - for (let address of items) { - const ProvenanceContract = new ethers.Contract(address, Provenance.abi, signer); - - const ProvenanceDetails = await ProvenanceContract.instrument() - const itemPhotos = await ProvenanceContract.getItemPics(); - const ProvenanceProps = {...ProvenanceDetails, itemPhotos} - - const index = ProvenanceContract.ownerCount(); - const ProvenanceOwnerInfo = await ProvenanceContract.ownerProvenance(index); - - const ProvenancePendingOwner = await ProvenanceContract.pendingOwner(); - - const ProvenanceCurrentOffer = await ProvenanceContract.currentOffer(); - - - provenanceArray.push({ - 'ProvenanceContract': ProvenanceContract, - 'ProvenanceProps': ProvenanceProps, - 'ProvenanceOwnerInfo': ProvenanceOwnerInfo, - 'ProvenancePendingOwner' : ProvenancePendingOwner, - 'ProvenanceCurrentOffer': ProvenanceCurrentOffer}) - } - setProvenanceObjects(provenanceArray); - } - - },[items, newProvenanceAddress, itemAdded, mainAccount]) - - const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); - - const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); - -// /** -// * @typedef {Object} Multihash -// * @property {string} digest The digest output of hash function in hex with prepended '0x' -// * @property {number} hashFunction The hash function code for the function used -// * @property {number} size The length of digest -// */ - -// /** -// * Partition multihash string into object representing multihash -// * -// * @param {string} multihash A base58 encoded multihash string -// * @returns {Multihash} -// */ -// function getBytes32FromMultihash(multihash) { -// const decoded = bs58.decode(multihash); - -// return { -// digest: `0x${decoded.slice(2).toString('hex')}`, -// hashFunction: decoded[0], -// size: decoded[1], -// }; -// } - -// /** -// * Encode a multihash structure into base58 encoded multihash string -// * -// * @param {Multihash} multihash -// * @returns {(string|null)} base58 encoded multihash string -// */ -// function getMultihashFromBytes32(multihash) { -// const { digest, hashFunction, size } = multihash; -// if (size === 0) return null; - -// // cut off leading "0x" -// const hashBytes = Buffer.from(digest.slice(2), 'hex'); - -// // prepend hashFunction and digest size -// const multihashBytes = new (hashBytes.constructor)(2 + hashBytes.length); -// multihashBytes[0] = hashFunction; -// multihashBytes[1] = size; -// multihashBytes.set(hashBytes, 2); - -// return bs58.encode(multihashBytes); -// } - -// /** -// * Parse Solidity response in array to a Multihash object -// * -// * @param {array} response Response array from Solidity -// * @returns {Multihash} multihash object -// */ - -// function parseContractResponse(response) { -// const [digest, hashFunction, size] = response; -// return { -// digest, -// hashFunction: hashFunction.toNumber(), -// size: size.toNumber(), -// }; -// } - -// /** -// * Parse Solidity response in array to a base58 encoded multihash string -// * -// * @param {array} response Response array from Solidity -// * @returns {string} base58 encoded multihash string -// */ -// function getMultihashFromContractResponse(response) { -// return getMultihashFromBytes32(parseContractResponse(response)); -// } - - - - - const state = { - items, - setItems, - itemAdded, - setItemAdded, - tokens, - setTokens, - provenanceObjects, - setProvenanceObjects, - newProvenanceAddress, - setNewProvenanceAddress, - ipfsGetterRootURL, - stringToBytes32, - bytes32ToString - }; - - return ( - - {children} - - ); + setProvenanceObjects(provenanceArray); + } + }, [items, newProvenanceAddress, itemAdded, mainAccount]); + + const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); + + const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); + + // /** + // * @typedef {Object} Multihash + // * @property {string} digest The digest output of hash function in hex with prepended '0x' + // * @property {number} hashFunction The hash function code for the function used + // * @property {number} size The length of digest + // */ + + // /** + // * Partition multihash string into object representing multihash + // * + // * @param {string} multihash A base58 encoded multihash string + // * @returns {Multihash} + // */ + // function getBytes32FromMultihash(multihash) { + // const decoded = bs58.decode(multihash); + + // return { + // digest: `0x${decoded.slice(2).toString('hex')}`, + // hashFunction: decoded[0], + // size: decoded[1], + // }; + // } + + // /** + // * Encode a multihash structure into base58 encoded multihash string + // * + // * @param {Multihash} multihash + // * @returns {(string|null)} base58 encoded multihash string + // */ + // function getMultihashFromBytes32(multihash) { + // const { digest, hashFunction, size } = multihash; + // if (size === 0) return null; + + // // cut off leading "0x" + // const hashBytes = Buffer.from(digest.slice(2), 'hex'); + + // // prepend hashFunction and digest size + // const multihashBytes = new (hashBytes.constructor)(2 + hashBytes.length); + // multihashBytes[0] = hashFunction; + // multihashBytes[1] = size; + // multihashBytes.set(hashBytes, 2); + + // return bs58.encode(multihashBytes); + // } + + // /** + // * Parse Solidity response in array to a Multihash object + // * + // * @param {array} response Response array from Solidity + // * @returns {Multihash} multihash object + // */ + + // function parseContractResponse(response) { + // const [digest, hashFunction, size] = response; + // return { + // digest, + // hashFunction: hashFunction.toNumber(), + // size: size.toNumber(), + // }; + // } + + // /** + // * Parse Solidity response in array to a base58 encoded multihash string + // * + // * @param {array} response Response array from Solidity + // * @returns {string} base58 encoded multihash string + // */ + // function getMultihashFromContractResponse(response) { + // return getMultihashFromBytes32(parseContractResponse(response)); + // } + + const state = { + items, + setItems, + itemAdded, + setItemAdded, + tokens, + setTokens, + provenanceObjects, + setProvenanceObjects, + newProvenanceAddress, + setNewProvenanceAddress, + ipfsGetterRootURL, + stringToBytes32, + bytes32ToString, + }; + + return {children}; } export function useItemContext() { return useContext(ItemContext); -} \ No newline at end of file +} diff --git a/src/context/TransferContext.js b/src/context/TransferContext.js index ad9a96a..efe71b7 100644 --- a/src/context/TransferContext.js +++ b/src/context/TransferContext.js @@ -1,63 +1,67 @@ // src/context/state.js -import { createContext, useContext, useState, useEffect } from 'react'; +import { createContext, useContext, useState, useEffect } from "react"; //ethers -import { ethers } from 'ethers'; +import { ethers } from "ethers"; //abi -import Provenance from '../../artifacts/contracts/Provenance.sol/Provenance.json'; +import Provenance from "../../artifacts/contracts/Provenance.sol/Provenance.json"; //context imports -import { useUserContext } from './UserContext.js'; -import { useContractContext} from './ContractContext.js' +import { useUserContext } from "./UserContext.js"; +import { useContractContext } from "./ContractContext.js"; - -const TransferContext= createContext(); +const TransferContext = createContext(); export function TransferContextProvider({ children }) { - const { MothershipContract } = useContractContext(); - const { mainAccount, provider, signer } = useUserContext(); - const [ pendingTransferContracts, setPendingTransferContracts ] = useState(); - - //load all pending transfers - useEffect(async() => { - if (MothershipContract) { - populateTransfers() - .then(result => console.log(result, "transfer populate result")) - .catch(error => console.log(error, 'populate error')); + const { MothershipContract } = useContractContext(); + const { mainAccount, provider, signer } = useUserContext(); + const [pendingTransferContracts, setPendingTransferContracts] = useState(); + + //load all pending transfers + useEffect(async () => { + if (MothershipContract) { + populateTransfers() + .then((result) => console.log(result, "transfer populate result")) + .catch((error) => console.log(error, "populate error")); + } + async function populateTransfers() { + let transferArray = []; + let transferItems; + + if (mainAccount) { + transferItems = await MothershipContract.getPendingTransfersOfBuyer( + mainAccount + ); + + for (let address of transferItems) { + const ProvenanceTransferContract = new ethers.Contract( + address, + Provenance.abi, + signer + ); + const ProvenanceTransferProps = + await ProvenanceTransferContract.instrument(); + const index = ProvenanceTransferContract.ownerCount(); + const ProvenanceOwnerInfo = + await ProvenanceTransferContract.ownerProvenance(index); + + transferArray.push({ + ProvenanceContract: ProvenanceTransferContract, + ProvenanceProps: ProvenanceTransferProps, + ProvenanceOwnerInfo: ProvenanceOwnerInfo, + }); + } } - async function populateTransfers() { - let transferArray = []; - let transferItems; - - if (mainAccount) { - transferItems = await MothershipContract.getPendingTransfersOfBuyer(mainAccount); - - - for (let address of transferItems) { - const ProvenanceTransferContract = new ethers.Contract(address, Provenance.abi, signer); - const ProvenanceTransferProps = await ProvenanceTransferContract.instrument() - const index = ProvenanceTransferContract.ownerCount(); - const ProvenanceOwnerInfo = await ProvenanceTransferContract.ownerProvenance(index); - - transferArray.push({'ProvenanceContract': ProvenanceTransferContract, 'ProvenanceProps': ProvenanceTransferProps, 'ProvenanceOwnerInfo': ProvenanceOwnerInfo}) - } - } - setPendingTransferContracts(transferArray); - // setTransferInitiated(false) - } - - },[MothershipContract, mainAccount]) - - - - - + setPendingTransferContracts(transferArray); + // setTransferInitiated(false) + } + }, [MothershipContract, mainAccount]); - const state = { - pendingTransferContracts, - setPendingTransferContracts - }; + const state = { + pendingTransferContracts, + setPendingTransferContracts, + }; return ( @@ -97,4 +101,4 @@ export function useTransferContext() { setPendingTransferContracts(transferArray); } - },[MothershipContract, mainAccount])*/ \ No newline at end of file + },[MothershipContract, mainAccount])*/ diff --git a/src/context/UserContext.js b/src/context/UserContext.js index 2e1c206..c8c7847 100644 --- a/src/context/UserContext.js +++ b/src/context/UserContext.js @@ -1,215 +1,180 @@ // src/context/state.js -import { createContext, useContext, useEffect, useState } from 'react'; +import { createContext, useContext, useEffect, useState } from "react"; // import { networkParams } from "./networks"; -import { toHex, truncateAddress } from '../hooks/utils'; +import { toHex, truncateAddress } from "../hooks/utils"; import { ethers } from "ethers"; - - - import Web3Modal from "web3modal"; -import { providerOptions } from '../components/web3Modal/providerOptions'; - - - - +import { providerOptions } from "../components/web3Modal/providerOptions"; // import useHandleEthereum from '../hooks/useHandleEthereumOriginal'; const UserContext = createContext(); - export function UserContextProvider({ children }) { - - const [web3Modal, setWeb3Modal]= useState(); - const [modalProvider, setModalProvider] = useState(); - const [provider, setProvider] = useState(); - const [mainAccount, setMainAccount] = useState(); - const [signer, setSigner] = useState(); - const [connectionErrorMessage, setConnectionErrorMessage] = useState('') - - - const [error, setError] = useState(""); - const [chainId, setChainId] = useState(); - const [network, setNetwork] = useState(); - - - - - //set web3Modal instance - - useEffect(async() => { - loadModal() - // console.log("load modal firing") - - async function loadModal() { - const web3ModalObj = new Web3Modal({ - cacheProvider: true, // optional - providerOptions // required - }); - setWeb3Modal(web3ModalObj); - } - - }, []); - - - - //connect page on reload - useEffect(async() => { - if (web3Modal && web3Modal.cachedProvider) { - connectWallet(); - } - - }, [web3Modal]) - - - - - const connectWallet = async () => { - try { - const modalProvider= await web3Modal.connect(); - const provider = new ethers.providers.Web3Provider(modalProvider); - const mainAccount = await provider.listAccounts(); - const network = await provider.getNetwork(); - setModalProvider(modalProvider); - setProvider(provider); - - const txnSigner = provider.getSigner() - setSigner(txnSigner) - - if (mainAccount) setMainAccount(mainAccount[0]); - setChainId(network.chainId); - - setConnectionErrorMessage(''); - - } catch (error) { - setConnectionErrorMessage("User denied connection. Please check your wallet provider and try again.") - console.error(error, "connect error"); - - } - - - }; - - - - const handleNetwork = (e) => { - const id = e.target.value; - setNetwork(Number(id)); - }; - - const handleInput = (e) => { - const msg = e.target.value; - setMessage(msg); - }; - +export function UserContextProvider({ children }) { + const [web3Modal, setWeb3Modal] = useState(); + const [modalProvider, setModalProvider] = useState(); + const [provider, setProvider] = useState(); + const [mainAccount, setMainAccount] = useState(); + const [signer, setSigner] = useState(); + const [connectionErrorMessage, setConnectionErrorMessage] = useState(""); + + const [error, setError] = useState(""); + const [chainId, setChainId] = useState(); + const [network, setNetwork] = useState(); + + //set web3Modal instance + + useEffect(async () => { + loadModal(); + // console.log("load modal firing") + + async function loadModal() { + const web3ModalObj = new Web3Modal({ + cacheProvider: true, // optional + providerOptions, // required + }); + setWeb3Modal(web3ModalObj); + } + }, []); - const switchNetwork = async () => { + //connect page on reload + useEffect(async () => { + if (web3Modal && web3Modal.cachedProvider) { + connectWallet(); + } + }, [web3Modal]); + + const connectWallet = async () => { + try { + const modalProvider = await web3Modal.connect(); + const provider = new ethers.providers.Web3Provider(modalProvider); + const mainAccount = await provider.listAccounts(); + const network = await provider.getNetwork(); + setModalProvider(modalProvider); + setProvider(provider); + + const txnSigner = provider.getSigner(); + setSigner(txnSigner); + + if (mainAccount) setMainAccount(mainAccount[0]); + setChainId(network.chainId); + + setConnectionErrorMessage(""); + } catch (error) { + setConnectionErrorMessage( + "User denied connection. Please check your wallet provider and try again." + ); + console.error(error, "connect error"); + } + }; + + const handleNetwork = (e) => { + const id = e.target.value; + setNetwork(Number(id)); + }; + + const handleInput = (e) => { + const msg = e.target.value; + setMessage(msg); + }; + + const switchNetwork = async () => { + try { + await provider.modalProvider.request({ + method: "wallet_switchEthereumChain", + params: [{ chainId: toHex(network) }], + }); + } catch (switchError) { + if (switchError.code === 4902) { try { - await provider.modalProvider.request({ - method: "wallet_switchEthereumChain", - params: [{ chainId: toHex(network) }] - }); - } catch (switchError) { - if (switchError.code === 4902) { - try { - await provider.modalProvider.request({ - method: "wallet_addEthereumChain", - params: [networkParams[toHex(network)]] - }); - } catch (error) { - setError(error); - } - } + await provider.modalProvider.request({ + method: "wallet_addEthereumChain", + params: [networkParams[toHex(network)]], + }); + } catch (error) { + setError(error); } - }; - - - // connect/disconnect - - const refreshState = () => { - setMainAccount(); - setSigner(); - setChainId(); - setNetwork(""); - }; - - - - const disconnect = async () => { - await web3Modal.clearCachedProvider(); - refreshState(); - console.log("disconnect") - }; - - - //EVENT LISTENERS - - useEffect(() => { - if (modalProvider?.on) { - - const handleMainAccountChanged = (mainAccount) => { - console.log("accountsChanged", mainAccount); - if (mainAccount) setMainAccount(mainAccount[0]); - }; - - const handleChainChanged = (_hexChainId) => { - setChainId(_hexChainId); - console.log(`Chain changed to:${_hexChainId}`) - }; - - const handleDisconnect = () => { - console.log("disconnect", error); - disconnect(); - }; - - modalProvider.on("accountsChanged", handleMainAccountChanged); - modalProvider.on("chainChanged", handleChainChanged); - modalProvider.on("disconnect", handleDisconnect); - - return () => { - if (modalProvider.removeListener) { - modalProvider.removeListener("accountsChanged", handleMainAccountChanged); - modalProvider.removeListener("chainChanged", handleChainChanged); - modalProvider.removeListener("disconnect", handleDisconnect); - } - }; + } + } + }; + + // connect/disconnect + + const refreshState = () => { + setMainAccount(); + setSigner(); + setChainId(); + setNetwork(""); + }; + + const disconnect = async () => { + await web3Modal.clearCachedProvider(); + refreshState(); + console.log("disconnect"); + }; + + //EVENT LISTENERS + + useEffect(() => { + if (modalProvider?.on) { + const handleMainAccountChanged = (mainAccount) => { + console.log("accountsChanged", mainAccount); + if (mainAccount) setMainAccount(mainAccount[0]); + }; + + const handleChainChanged = (_hexChainId) => { + setChainId(_hexChainId); + console.log(`Chain changed to:${_hexChainId}`); + }; + + const handleDisconnect = () => { + console.log("disconnect", error); + disconnect(); + }; + + modalProvider.on("accountsChanged", handleMainAccountChanged); + modalProvider.on("chainChanged", handleChainChanged); + modalProvider.on("disconnect", handleDisconnect); + + return () => { + if (modalProvider.removeListener) { + modalProvider.removeListener( + "accountsChanged", + handleMainAccountChanged + ); + modalProvider.removeListener("chainChanged", handleChainChanged); + modalProvider.removeListener("disconnect", handleDisconnect); } - }, [modalProvider]); - - - - const ipfsGetterRootURL = "https://gateway.pinata.cloud/ipfs/"; - const date = new Date(); - const dateString = date.toLocaleDateString(); - - - - - - const state = { - mainAccount, - setMainAccount, - chainId, - modalProvider, - provider, - signer, - connectWallet, - connectionErrorMessage, - setConnectionErrorMessage, - disconnect, - switchNetwork, - dateString, - ipfsGetterRootURL} - - // console.log(signer, "signer in UserContext", state, "state in UC") - - return ( - - {children} - - ); + }; } + }, [modalProvider]); + + const ipfsGetterRootURL = "https://gateway.pinata.cloud/ipfs/"; + const date = new Date(); + const dateString = date.toLocaleDateString(); + + const state = { + mainAccount, + setMainAccount, + chainId, + modalProvider, + provider, + signer, + connectWallet, + connectionErrorMessage, + setConnectionErrorMessage, + disconnect, + switchNetwork, + dateString, + ipfsGetterRootURL, + }; + + // console.log(signer, "signer in UserContext", state, "state in UC") + + return {children}; +} export function useUserContext() { return useContext(UserContext); -} \ No newline at end of file +} diff --git a/src/deployedContractAddresses/instrumenttokenaddress.json b/src/deployedContractAddresses/instrumenttokenaddress.json index 8d93c59..3c945a4 100644 --- a/src/deployedContractAddresses/instrumenttokenaddress.json +++ b/src/deployedContractAddresses/instrumenttokenaddress.json @@ -1 +1 @@ -{ "address": "0x663F3ad617193148711d28f5334eE4Ed07016602" } \ No newline at end of file +{ "address": "0x5FbDB2315678afecb367f032d93F642f64180aa3" } \ No newline at end of file diff --git a/src/hooks/utils.js b/src/hooks/utils.js index b769288..11817b2 100644 --- a/src/hooks/utils.js +++ b/src/hooks/utils.js @@ -1,14 +1,13 @@ export const truncateAddress = (address) => { - if (!address) return "No Account"; - const match = address.match( - /^(0x[a-zA-Z0-9]{2})[a-zA-Z0-9]+([a-zA-Z0-9]{2})$/ - ); - if (!match) return address; - return `${match[1]}…${match[2]}`; - }; - - export const toHex = (num) => { - const val = Number(num); - return "0x" + val.toString(16); - }; - \ No newline at end of file + if (!address) return "No Account"; + const match = address.match( + /^(0x[a-zA-Z0-9]{2})[a-zA-Z0-9]+([a-zA-Z0-9]{2})$/ + ); + if (!match) return address; + return `${match[1]}…${match[2]}`; +}; + +export const toHex = (num) => { + const val = Number(num); + return "0x" + val.toString(16); +}; diff --git a/styles/global.css b/styles/global.css index 751bb54..daf66f8 100644 --- a/styles/global.css +++ b/styles/global.css @@ -7,7 +7,6 @@ pointer-events: none; } - .modal-backdrop { z-index: 0; } @@ -15,4 +14,3 @@ h1 h2 h3 h4 p { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } - diff --git a/test/BuyerTransferTests.js b/test/BuyerTransferTests.js index e41c16b..545e08a 100644 --- a/test/BuyerTransferTests.js +++ b/test/BuyerTransferTests.js @@ -1,104 +1,126 @@ const { expect } = require("chai"); const { ethers, waffle } = require("hardhat"); -const ProvenanceABI = require('../artifacts/contracts/Provenance.sol/Provenance.json'); - +const ProvenanceABI = require("../artifacts/contracts/Provenance.sol/Provenance.json"); //const provider = new ethers.providers.Web3Provider(window.ethereum); //const signer = provider.getSigner(0); describe("Buyer Initiated Transfer Tests", function () { - - let TokenContract; - let MothershipContract; - let ProvenanceContractTest; - let owner; - let addr1; - let addr2; - let addr3; - const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); - const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); - - beforeEach(async function () { - const IDTC = await ethers.getContractFactory('InstrumentDeedToken'); - [owner, addr1, addr2, addr3] = await ethers.getSigners(); - TokenContract = await IDTC.deploy(); - - MSC = await ethers.getContractFactory('Mothership'); - MothershipContract = await MSC.deploy(TokenContract.address); - - await TokenContract.safeMint(addr1.address, "www.google.com"); - const result = await MothershipContract.connect(addr1).createNewProvenance( - 1, - stringToBytes32('serial#'), - stringToBytes32('Selmer'), - stringToBytes32('SBA'), - 1957, - 0, - stringToBytes32("12/31/1999"), - 'ipfs', - ["ipfs"]) - let receipt = await result.wait() - let event = await receipt.events?.filter((x) => {return x.event == "ProvenanceCreated"}); - let ProvenanceAddress = event[0].args.childAddress; - // prepare transfer - ProvenanceContractTest = new ethers.Contract(ProvenanceAddress, ProvenanceABI.abi, addr1); - await TokenContract.connect(addr1).approve(ProvenanceContractTest.address, 0) - - - }); - - it("owner cannot make offer on own Provenance", async function() { - await expect(ProvenanceContractTest.connect(addr1).makeOffer({value: 1000000 })).to.be.revertedWith( "VM Exception while processing transaction: reverted with reason string 'You cannot purchase your own Provenance'"); + let TokenContract; + let MothershipContract; + let ProvenanceContractTest; + let owner; + let addr1; + let addr2; + let addr3; + const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); + const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); + + beforeEach(async function () { + const IDTC = await ethers.getContractFactory("InstrumentDeedToken"); + [owner, addr1, addr2, addr3] = await ethers.getSigners(); + TokenContract = await IDTC.deploy(); + + MSC = await ethers.getContractFactory("Mothership"); + MothershipContract = await MSC.deploy(TokenContract.address); + + await TokenContract.safeMint(addr1.address, "www.google.com"); + const result = await MothershipContract.connect(addr1).createNewProvenance( + 1, + stringToBytes32("serial#"), + stringToBytes32("Selmer"), + stringToBytes32("SBA"), + 1957, + 0, + stringToBytes32("12/31/1999"), + "ipfs", + ["ipfs"] + ); + let receipt = await result.wait(); + let event = await receipt.events?.filter((x) => { + return x.event == "ProvenanceCreated"; }); + let ProvenanceAddress = event[0].args.childAddress; + // prepare transfer + ProvenanceContractTest = new ethers.Contract( + ProvenanceAddress, + ProvenanceABI.abi, + addr1 + ); + await TokenContract.connect(addr1).approve( + ProvenanceContractTest.address, + 0 + ); + }); - it("buyer makes offer on Provenance", async function() { + it("owner cannot make offer on own Provenance", async function () { + await expect( + ProvenanceContractTest.connect(addr1).makeOffer({ value: 1000000 }) + ).to.be.revertedWith( + "VM Exception while processing transaction: reverted with reason string 'You cannot purchase your own Provenance'" + ); + }); - await ProvenanceContractTest.connect(addr2).makeOffer({value: 1000000 }); + it("buyer makes offer on Provenance", async function () { + await ProvenanceContractTest.connect(addr2).makeOffer({ value: 1000000 }); - let currentOffer = await ProvenanceContractTest.currentOffer(); + let currentOffer = await ProvenanceContractTest.currentOffer(); - expect (currentOffer.buyer).to.equal(addr2.address); - expect (currentOffer.offer).to.equal(1000000); - - }); + expect(currentOffer.buyer).to.equal(addr2.address); + expect(currentOffer.offer).to.equal(1000000); + }); - it("buyer cannot make offer lower than existing offer", async function() { - await ProvenanceContractTest.connect(addr2).makeOffer({value: 1000000 }); - await expect(ProvenanceContractTest.connect(addr3).makeOffer({value: 100 })).to.be.revertedWith( "VM Exception while processing transaction: reverted with reason string 'Offer must greater than current standing offer'"); - }); + it("buyer cannot make offer lower than existing offer", async function () { + await ProvenanceContractTest.connect(addr2).makeOffer({ value: 1000000 }); + await expect( + ProvenanceContractTest.connect(addr3).makeOffer({ value: 100 }) + ).to.be.revertedWith( + "VM Exception while processing transaction: reverted with reason string 'Offer must greater than current standing offer'" + ); + }); - it("owner or buyer can cancel existing offer", async function() { - const provider = waffle.provider; + it("owner or buyer can cancel existing offer", async function () { + const provider = waffle.provider; - await ProvenanceContractTest.connect(addr2).makeOffer({value: 100000000000 }); - const balanceInWeiBeforeCancel = await provider.getBalance(ProvenanceContractTest.address); - expect (balanceInWeiBeforeCancel).to.equal(100000000000) - await ProvenanceContractTest.cancelOffer() - const balanceInWeiAfterCancel = await provider.getBalance(ProvenanceContractTest.address); - expect (balanceInWeiAfterCancel).to.equal(0) + await ProvenanceContractTest.connect(addr2).makeOffer({ + value: 100000000000, }); + const balanceInWeiBeforeCancel = await provider.getBalance( + ProvenanceContractTest.address + ); + expect(balanceInWeiBeforeCancel).to.equal(100000000000); + await ProvenanceContractTest.cancelOffer(); + const balanceInWeiAfterCancel = await provider.getBalance( + ProvenanceContractTest.address + ); + expect(balanceInWeiAfterCancel).to.equal(0); + }); - it("owner accepts offer", async function() { - await ProvenanceContractTest.connect(addr2).makeOffer({value: 100000000000 }); - expect ((await ProvenanceContractTest.currentOffer()).buyer).to.equal(addr2.address); - await ProvenanceContractTest.connect(addr1).acceptOffer(); - expect ((await ProvenanceContractTest.currentOffer()).buyer).to.equal(ethers.constants.AddressZero); - - - - - expect((await ProvenanceContractTest.currentOffer()).buyer).to.equal(ethers.constants.AddressZero); - expect((await ProvenanceContractTest.currentOffer()).offer).to.equal(0); - - let ownerNumber = await ProvenanceContractTest.ownerCount(); - - - expect((await ProvenanceContractTest.ownerProvenance(ownerNumber)).ownerAddress).to.equal(addr2.address) - expect((await ProvenanceContractTest.ownerProvenance(ownerNumber - 1)).ownerAddress).to.equal(addr1.address) - - + it("owner accepts offer", async function () { + await ProvenanceContractTest.connect(addr2).makeOffer({ + value: 100000000000, }); - - - + expect((await ProvenanceContractTest.currentOffer()).buyer).to.equal( + addr2.address + ); + await ProvenanceContractTest.connect(addr1).acceptOffer(); + expect((await ProvenanceContractTest.currentOffer()).buyer).to.equal( + ethers.constants.AddressZero + ); + + expect((await ProvenanceContractTest.currentOffer()).buyer).to.equal( + ethers.constants.AddressZero + ); + expect((await ProvenanceContractTest.currentOffer()).offer).to.equal(0); + + let ownerNumber = await ProvenanceContractTest.ownerCount(); + + expect( + (await ProvenanceContractTest.ownerProvenance(ownerNumber)).ownerAddress + ).to.equal(addr2.address); + expect( + (await ProvenanceContractTest.ownerProvenance(ownerNumber - 1)) + .ownerAddress + ).to.equal(addr1.address); }); +}); diff --git a/test/Mothership-TransferTests.js b/test/Mothership-TransferTests.js index 63db694..4913828 100644 --- a/test/Mothership-TransferTests.js +++ b/test/Mothership-TransferTests.js @@ -1,78 +1,74 @@ const { expect } = require("chai"); const { ethers } = require("hardhat"); -const ProvenanceABI = require('../artifacts/contracts/Provenance.sol/Provenance.json'); - +const ProvenanceABI = require("../artifacts/contracts/Provenance.sol/Provenance.json"); //const provider = new ethers.providers.Web3Provider(window.ethereum); //const signer = provider.getSigner(0); describe("Mothership-Transfer Tests", function () { - - let TokenContract; - let MothershipContract; - let ProvenanceContract; - let owner; - let addr1; - let addr2; - let addr3; - let addr4; - const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); - const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); + let TokenContract; + let MothershipContract; + let ProvenanceContract; + let owner; + let addr1; + let addr2; + let addr3; + let addr4; + const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); + const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); - before(async function () { - const IDTC = await ethers.getContractFactory('InstrumentDeedToken'); - [owner, addr1, addr2, addr3, addr4] = await ethers.getSigners(); - TokenContract = await IDTC.deploy(); + before(async function () { + const IDTC = await ethers.getContractFactory("InstrumentDeedToken"); + [owner, addr1, addr2, addr3, addr4] = await ethers.getSigners(); + TokenContract = await IDTC.deploy(); - MSC = await ethers.getContractFactory('Mothership'); - MothershipContract = await MSC.deploy(TokenContract.address); + MSC = await ethers.getContractFactory("Mothership"); + MothershipContract = await MSC.deploy(TokenContract.address); - await TokenContract.safeMint(addr1.address, "www.google.com") + await TokenContract.safeMint(addr1.address, "www.google.com"); - const result = await MothershipContract.connect(addr1).createNewProvenance( - 1, - stringToBytes32('serial#'), - stringToBytes32('Selmer'), - stringToBytes32('SBA'), - stringToBytes32('1957'), - 0, - stringToBytes32("12/31/1999"), - 'ipfs', - ["ipfs"]) - let receipt = await result.wait() - let event = await receipt.events?.filter((x) => {return x.event == "ProvenanceCreated"}); - let ProvenanceAddress = event[0].args.childAddress; - ProvenanceContract = new ethers.Contract(ProvenanceAddress, ProvenanceABI.abi, addr1); - + const result = await MothershipContract.connect(addr1).createNewProvenance( + 1, + stringToBytes32("serial#"), + stringToBytes32("Selmer"), + stringToBytes32("SBA"), + stringToBytes32("1957"), + 0, + stringToBytes32("12/31/1999"), + "ipfs", + ["ipfs"] + ); + let receipt = await result.wait(); + let event = await receipt.events?.filter((x) => { + return x.event == "ProvenanceCreated"; }); + let ProvenanceAddress = event[0].args.childAddress; + ProvenanceContract = new ethers.Contract( + ProvenanceAddress, + ProvenanceABI.abi, + addr1 + ); + }); - - - - - // it("claimOwnership of from one owner to another", async function() { - - // await TokenContract.connect(addr1).approve(ProvenanceContract.address, 0) - // await ProvenanceContract.claimOwnership(addr2.address, 'owner2name', 'verificationPhoto2'); - // expect(await MothershipContract.ownersToAxes(addr2.address, 0)).to.equal(ProvenanceContract.address) - - // }); - + // it("claimOwnership of from one owner to another", async function() { - // it("retrieve previous owner information", async function() { + // await TokenContract.connect(addr1).approve(ProvenanceContract.address, 0) + // await ProvenanceContract.claimOwnership(addr2.address, 'owner2name', 'verificationPhoto2'); + // expect(await MothershipContract.ownersToAxes(addr2.address, 0)).to.equal(ProvenanceContract.address) - // //sell to address 3 - // await TokenContract.connect(addr2).approve(ProvenanceContract.address, 0) - // await ProvenanceContract.connect(addr2).claimOwnership(addr3.address, 'owner3name', 'verificationPhoto3'); - // //sell to address 4 - // await TokenContract.connect(addr3).approve(ProvenanceContract.address, 0) - // await ProvenanceContract.connect(addr3).claimOwnership(addr4.address, 'owner4name', 'verificationPhoto4'); - // }) - + // }); + // it("retrieve previous owner information", async function() { - }); + // //sell to address 3 + // await TokenContract.connect(addr2).approve(ProvenanceContract.address, 0) + // await ProvenanceContract.connect(addr2).claimOwnership(addr3.address, 'owner3name', 'verificationPhoto3'); + // //sell to address 4 + // await TokenContract.connect(addr3).approve(ProvenanceContract.address, 0) + // await ProvenanceContract.connect(addr3).claimOwnership(addr4.address, 'owner4name', 'verificationPhoto4'); + // }) +}); - /* +/* - expect(await ProvenanceContract.owner()).to.equal(owner);*/ \ No newline at end of file + expect(await ProvenanceContract.owner()).to.equal(owner);*/ diff --git a/test/MothershipTests.js b/test/MothershipTests.js index a8ef0a5..f593060 100644 --- a/test/MothershipTests.js +++ b/test/MothershipTests.js @@ -1,238 +1,278 @@ const { expect } = require("chai"); const { ethers } = require("hardhat"); -const ProvenanceABI = require('../artifacts/contracts/Provenance.sol/Provenance.json'); - +const ProvenanceABI = require("../artifacts/contracts/Provenance.sol/Provenance.json"); //const provider = new ethers.providers.Web3Provider(window.ethereum); //const signer = provider.getSigner(0); describe("Mothership Tests", function () { - - let TokenContract; - let MothershipContract; - - const provider = waffle.provider; + let TokenContract; + let MothershipContract; - let owner; - let addr1; - let addr2; - const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); - const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); + const provider = waffle.provider; - beforeEach(async function () { - const IDTC = await ethers.getContractFactory('InstrumentDeedToken'); - [owner, addr1, addr2] = await ethers.getSigners(); - TokenContract = await IDTC.deploy(); + let owner; + let addr1; + let addr2; + const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); + const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); - MSC = await ethers.getContractFactory('Mothership'); - MothershipContract = await MSC.deploy(TokenContract.address); + beforeEach(async function () { + const IDTC = await ethers.getContractFactory("InstrumentDeedToken"); + [owner, addr1, addr2] = await ethers.getSigners(); + TokenContract = await IDTC.deploy(); - await TokenContract.safeMint(addr1.address, "www.google.com") - - }); + MSC = await ethers.getContractFactory("Mothership"); + MothershipContract = await MSC.deploy(TokenContract.address); - it("verify token minted to address1", async function() { - expect(await TokenContract.ownerOf(0)).to.equal(addr1.address); - expect(await TokenContract.balanceOf(addr1.address)).to.equal(1); - expect(await TokenContract.tokenURI(0)).to.equal("www.google.com"); - }); + await TokenContract.safeMint(addr1.address, "www.google.com"); + }); - it("Mothership connected Token Address", async function() { + it("verify token minted to address1", async function () { + expect(await TokenContract.ownerOf(0)).to.equal(addr1.address); + expect(await TokenContract.balanceOf(addr1.address)).to.equal(1); + expect(await TokenContract.tokenURI(0)).to.equal("www.google.com"); + }); - expect(await MothershipContract.instrumentDeedTokenContract()).to.equal(TokenContract.address); - }); + it("Mothership connected Token Address", async function () { + expect(await MothershipContract.instrumentDeedTokenContract()).to.equal( + TokenContract.address + ); + }); - it("deploy Provenance from Mothership", async function() { - //console.log(addr1.address,"addr1.address") - //console.log(MothershipContract.address, "MothershipContract address") - - - const result = await MothershipContract.connect(addr1).createNewProvenance(1, - ethers.utils.formatBytes32String('serial'), - ethers.utils.formatBytes32String('Selmer'), - ethers.utils.formatBytes32String('SBA'), - 1957, 0, - ethers.utils.formatBytes32String("12/31/2000"), - ethers.utils.formatBytes32String('ipfs'), - [ethers.utils.formatBytes32String("ipfs")]) - let receipt = await result.wait() - let event = await receipt.events?.filter((x) => {return x.event == "ProvenanceCreated"}); - let ProvenanceAddress = event[0].args.childAddress; - ProvenanceContract = new ethers.Contract(ProvenanceAddress, ProvenanceABI.abi, addr1); - - //Sample Provenance Trait - expect((await ProvenanceContract.instrument()).serial).to.equal(ethers.utils.formatBytes32String('serial')); - //Mothership remains Provenance 'owner' - expect(await ProvenanceContract.owner()).to.equal(MothershipContract.address); - //Provenance listed owner is owner of item - expect((await ProvenanceContract.ownerProvenance(1)).ownerAddress).to.equal(addr1.address) - //expect((await ProvenanceContract.instrumentOwner()).ownerAddress).to.equal(addr1.address); - + it("deploy Provenance from Mothership", async function () { + //console.log(addr1.address,"addr1.address") + //console.log(MothershipContract.address, "MothershipContract address") + + const result = await MothershipContract.connect(addr1).createNewProvenance( + 1, + ethers.utils.formatBytes32String("serial"), + ethers.utils.formatBytes32String("Selmer"), + ethers.utils.formatBytes32String("SBA"), + 1957, + 0, + ethers.utils.formatBytes32String("12/31/2000"), + ethers.utils.formatBytes32String("ipfs"), + [ethers.utils.formatBytes32String("ipfs")] + ); + let receipt = await result.wait(); + let event = await receipt.events?.filter((x) => { + return x.event == "ProvenanceCreated"; }); + let ProvenanceAddress = event[0].args.childAddress; + ProvenanceContract = new ethers.Contract( + ProvenanceAddress, + ProvenanceABI.abi, + addr1 + ); + + //Sample Provenance Trait + expect((await ProvenanceContract.instrument()).serial).to.equal( + ethers.utils.formatBytes32String("serial") + ); + //Mothership remains Provenance 'owner' + expect(await ProvenanceContract.owner()).to.equal( + MothershipContract.address + ); + //Provenance listed owner is owner of item + expect((await ProvenanceContract.ownerProvenance(1)).ownerAddress).to.equal( + addr1.address + ); + //expect((await ProvenanceContract.instrumentOwner()).ownerAddress).to.equal(addr1.address); + }); - it("sale of from one owner to another", async function() { - let ProvenanceContractTest; - - - const result = await MothershipContract.connect(addr1).createNewProvenance(1, - ethers.utils.formatBytes32String('serial'), - ethers.utils.formatBytes32String('Selmer'), - ethers.utils.formatBytes32String('SBA'), - 1957, 0, - ethers.utils.formatBytes32String("12/31/2000"), - ethers.utils.formatBytes32String('ipfs'), - [ethers.utils.formatBytes32String("ipfs")]) - let receipt = await result.wait() - let event = await receipt.events?.filter((x) => {return x.event == "ProvenanceCreated"}); - let ProvenanceAddress = event[0].args.childAddress; - - - - // prepare transfer - ProvenanceContractTest = new ethers.Contract(ProvenanceAddress, ProvenanceABI.abi, addr1); - - await TokenContract.connect(addr1).approve(ProvenanceContractTest.address, 0) - // console.log(await TokenContract.getApproved(0), "approved for token zero") - await ProvenanceContractTest.connect(addr1).setPendingOwner(addr2.address) - - - // original owner of provenance - let provenanceOwner = (await ProvenanceContractTest.ownerProvenance(1)).ownerAddress; - - - //need to break this into other tests and also make sure that old provenance is removed from the old owner - const ProvenanceContractTestSecondSigner = ProvenanceContractTest.connect(addr2); - - - await ProvenanceContractTestSecondSigner.connect(addr2).claimOwnership(provenanceOwner, ethers.utils.formatBytes32String('verificationPhoto2'),ethers.utils.formatBytes32String('12/31/1999')); - - - const updatedAddr1Provs = await MothershipContract.connect(addr1).getOwnersInstruments(); - const updatedAddr2Provs = await MothershipContract.connect(addr2).getOwnersInstruments(); - - - - expect(updatedAddr1Provs).to.deep.equal([]) - expect(updatedAddr2Provs[0]).to.equal(ProvenanceContractTest.address) - - expect((await ProvenanceContractTestSecondSigner.ownerProvenance(2)).ownerAddress).to.equal(addr2.address); - expect((await ProvenanceContractTestSecondSigner.ownerProvenance(1)).ownerAddress).to.equal(addr1.address); - - + it("sale of from one owner to another", async function () { + let ProvenanceContractTest; + + const result = await MothershipContract.connect(addr1).createNewProvenance( + 1, + ethers.utils.formatBytes32String("serial"), + ethers.utils.formatBytes32String("Selmer"), + ethers.utils.formatBytes32String("SBA"), + 1957, + 0, + ethers.utils.formatBytes32String("12/31/2000"), + ethers.utils.formatBytes32String("ipfs"), + [ethers.utils.formatBytes32String("ipfs")] + ); + let receipt = await result.wait(); + let event = await receipt.events?.filter((x) => { + return x.event == "ProvenanceCreated"; }); + let ProvenanceAddress = event[0].args.childAddress; + + // prepare transfer + ProvenanceContractTest = new ethers.Contract( + ProvenanceAddress, + ProvenanceABI.abi, + addr1 + ); + + await TokenContract.connect(addr1).approve( + ProvenanceContractTest.address, + 0 + ); + // console.log(await TokenContract.getApproved(0), "approved for token zero") + await ProvenanceContractTest.connect(addr1).setPendingOwner(addr2.address); + + // original owner of provenance + let provenanceOwner = (await ProvenanceContractTest.ownerProvenance(1)) + .ownerAddress; + + //need to break this into other tests and also make sure that old provenance is removed from the old owner + const ProvenanceContractTestSecondSigner = + ProvenanceContractTest.connect(addr2); + + await ProvenanceContractTestSecondSigner.connect(addr2).claimOwnership( + provenanceOwner, + ethers.utils.formatBytes32String("verificationPhoto2"), + ethers.utils.formatBytes32String("12/31/1999") + ); + + const updatedAddr1Provs = await MothershipContract.connect( + addr1 + ).getOwnersInstruments(); + const updatedAddr2Provs = await MothershipContract.connect( + addr2 + ).getOwnersInstruments(); + + expect(updatedAddr1Provs).to.deep.equal([]); + expect(updatedAddr2Provs[0]).to.equal(ProvenanceContractTest.address); + + expect( + (await ProvenanceContractTestSecondSigner.ownerProvenance(2)).ownerAddress + ).to.equal(addr2.address); + expect( + (await ProvenanceContractTestSecondSigner.ownerProvenance(1)).ownerAddress + ).to.equal(addr1.address); + }); - it("get all Provenances from All Owners", async function() { - const type = [0, 1, 2] - const brand = ["Jupiter", "Yamaha", "JL Woodwinds", "Yanigisawa", "Antigua Winds", "Pearl", "Selmer", "Buffet"] - const model = ["Mark VI", "SBA", "R13", "Bronze Series", "Cigar Cutter", "Balanced Action", "King 20", "Silver Fox"] - const images = ["QmNvzkSMNCF9bRry5CHiTCnz7s8Fc6ooNVQyuFc4EPDaQV", "QmPYABsoen4yRJWp4ta7yrhxgsqNEQLK15c68BL6BQrQAW", "QmQ4wfPDcxJeLcypv6JQt6757Nmzi95k5wZbjjFYzjsUW2", "QmdBEnkC1qXc1pZsGkRTPkwhqohGyiTd7tZKvD1v8VQ65Y"] - - function serial() { - return Math.floor(Math.random() * 1000); - } - - function tokenID() { - return Math.floor(Math.random() * 100) - } - function random_element(items) - { return items[Math.floor(Math.random()*items.length)]; - } - function randomDate(start, end) { - const dateStr = new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); - return dateStr.toLocaleDateString(); - } - function randomYear(start, end) { - return new Date(start.getTime() + Math.random() * (end.getTime() - start.getTime())); - } - - const batchMintTokens = () => { - TokenContract.batchMint(100, random_element(images)) - } - - const create10Provenances = async() => { - - - let type1 = random_element(type); - let serial1 = stringToBytes32((serial()).toString()); - let brand1 = stringToBytes32(random_element(brand)); - let model1 = stringToBytes32(random_element(model)); - let year1 = (randomYear(new Date(1897, 0, 1), new Date())).getFullYear(); - let tokenID1 = tokenID(); - let date1 = stringToBytes32(((randomDate(new Date(1897, 0, 1), new Date()))).toString()); - - let vImages1 = random_element(images); - - - - await MothershipContract.connect(addr1).createBatchProvenances( - type1, - serial1, - brand1, - model1, - year1, - tokenID1, - date1, - vImages1, - images) - .then(async(result) => { - provider.waitForTransaction(result.hash) - .then(mined => { - if (mined) { - MothershipContract.once("ProvenanceCreated", (type, newAddress) => { - - console.log(`CreateBatchProvenances(11) a success!`) - - })} - }) - - }) - .catch(error => console.log(error, "error")) - + it("get all Provenances from All Owners", async function () { + const type = [0, 1, 2]; + const brand = [ + "Jupiter", + "Yamaha", + "JL Woodwinds", + "Yanigisawa", + "Antigua Winds", + "Pearl", + "Selmer", + "Buffet", + ]; + const model = [ + "Mark VI", + "SBA", + "R13", + "Bronze Series", + "Cigar Cutter", + "Balanced Action", + "King 20", + "Silver Fox", + ]; + const images = [ + "QmNvzkSMNCF9bRry5CHiTCnz7s8Fc6ooNVQyuFc4EPDaQV", + "QmPYABsoen4yRJWp4ta7yrhxgsqNEQLK15c68BL6BQrQAW", + "QmQ4wfPDcxJeLcypv6JQt6757Nmzi95k5wZbjjFYzjsUW2", + "QmdBEnkC1qXc1pZsGkRTPkwhqohGyiTd7tZKvD1v8VQ65Y", + ]; + + function serial() { + return Math.floor(Math.random() * 1000); + } + + function tokenID() { + return Math.floor(Math.random() * 100); + } + function random_element(items) { + return items[Math.floor(Math.random() * items.length)]; + } + function randomDate(start, end) { + const dateStr = new Date( + start.getTime() + Math.random() * (end.getTime() - start.getTime()) + ); + return dateStr.toLocaleDateString(); + } + function randomYear(start, end) { + return new Date( + start.getTime() + Math.random() * (end.getTime() - start.getTime()) + ); + } + + const batchMintTokens = () => { + TokenContract.batchMint(100, random_element(images)); + }; + + const create10Provenances = async () => { + let type1 = random_element(type); + let serial1 = stringToBytes32(serial().toString()); + let brand1 = stringToBytes32(random_element(brand)); + let model1 = stringToBytes32(random_element(model)); + let year1 = randomYear(new Date(1897, 0, 1), new Date()).getFullYear(); + let tokenID1 = tokenID(); + let date1 = stringToBytes32( + randomDate(new Date(1897, 0, 1), new Date()).toString() + ); + + let vImages1 = random_element(images); + + await MothershipContract.connect(addr1) + .createBatchProvenances( + type1, + serial1, + brand1, + model1, + year1, + tokenID1, + date1, + vImages1, + images + ) + .then(async (result) => { + provider.waitForTransaction(result.hash).then((mined) => { + if (mined) { + MothershipContract.once( + "ProvenanceCreated", + (type, newAddress) => { + console.log(`CreateBatchProvenances(11) a success!`); + } + ); } + }); + }) + .catch((error) => console.log(error, "error")); + }; - await batchMintTokens() + await batchMintTokens(); - for (var i = 1; i < 8; i++) create10Provenances(); + for (var i = 1; i < 8; i++) create10Provenances(); - let ownerArray = await MothershipContract.getOwners(); - - expect (ownerArray[0]).to.equal(addr1.address) - let allAxes = await MothershipContract.getAllProvenances(); + let ownerArray = await MothershipContract.getOwners(); + expect(ownerArray[0]).to.equal(addr1.address); + let allAxes = await MothershipContract.getAllProvenances(); + }); - + // it("claimOwnership of from one owner to another", async function() { - - + // const result = await MothershipContract.connect(addr1).createNewProvenance(1, 'serial#', 'Selmer', 'SBA', 1957, 0, 'ipfs', 'John', ["ipfs"]) + // let receipt = await result.wait() + // let event = await receipt.events?.filter((x) => {return x.event == "ProvenanceCreated"}); + // let ProvenanceAddress = event[0].args.childAddress; + // ProvenanceContract = new ethers.Contract(ProvenanceAddress, ProvenanceABI.abi, addr1); - }); + // await TokenContract.connect(addr1).approve(ProvenanceContract.address, 0) + // await ProvenanceContract.claimOwnership(addr2.address, 'owner2name', 'verificationPhoto2'); + // expect(await MothershipContract.ownersToAxes(addr2.address, 0)).to.equal(ProvenanceContract.address) - - - // it("claimOwnership of from one owner to another", async function() { - - // const result = await MothershipContract.connect(addr1).createNewProvenance(1, 'serial#', 'Selmer', 'SBA', 1957, 0, 'ipfs', 'John', ["ipfs"]) - // let receipt = await result.wait() - // let event = await receipt.events?.filter((x) => {return x.event == "ProvenanceCreated"}); - // let ProvenanceAddress = event[0].args.childAddress; - // ProvenanceContract = new ethers.Contract(ProvenanceAddress, ProvenanceABI.abi, addr1); - - // await TokenContract.connect(addr1).approve(ProvenanceContract.address, 0) - // await ProvenanceContract.claimOwnership(addr2.address, 'owner2name', 'verificationPhoto2'); - // expect(await MothershipContract.ownersToAxes(addr2.address, 0)).to.equal(ProvenanceContract.address) - - // }); - - - // it("retrieve previous owner information", async function() { - - // //sell to address 3 - // await TokenContract.connect(addr2).approve(ProvenanceContract.address, 0) - // await ProvenanceContract.connect(addr2).claimOwnership(addr3.address, 'owner3name', 'verificationPhoto3'); - // //sell to address 4 - // await TokenContract.connect(addr3).approve(ProvenanceContract.address, 0) - // await ProvenanceContract.connect(addr3).claimOwnership(addr4.address, 'owner4name', 'verificationPhoto4'); - // }) - + // }); - }); + // it("retrieve previous owner information", async function() { + + // //sell to address 3 + // await TokenContract.connect(addr2).approve(ProvenanceContract.address, 0) + // await ProvenanceContract.connect(addr2).claimOwnership(addr3.address, 'owner3name', 'verificationPhoto3'); + // //sell to address 4 + // await TokenContract.connect(addr3).approve(ProvenanceContract.address, 0) + // await ProvenanceContract.connect(addr3).claimOwnership(addr4.address, 'owner4name', 'verificationPhoto4'); + // }) +}); diff --git a/test/ProvenanceTests.js b/test/ProvenanceTests.js index 6af77b8..5c6cca6 100644 --- a/test/ProvenanceTests.js +++ b/test/ProvenanceTests.js @@ -1,129 +1,149 @@ const { expect } = require("chai"); const { ethers } = require("hardhat"); -const ProvenanceABI = require('../artifacts/contracts/Provenance.sol/Provenance.json'); - +const ProvenanceABI = require("../artifacts/contracts/Provenance.sol/Provenance.json"); //const provider = new ethers.providers.Web3Provider(window.ethereum); //const signer = provider.getSigner(0); describe("Provenance Tests", function () { - - let TokenContract; - let MothershipContract; - //let ProvenanceContract; - let owner; - let addr1; - let addr2; - const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); - const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); - - beforeEach(async function () { - const IDTC = await ethers.getContractFactory('InstrumentDeedToken'); - [owner, addr1, addr2] = await ethers.getSigners(); - TokenContract = await IDTC.deploy(); - - MSC = await ethers.getContractFactory('Mothership'); - MothershipContract = await MSC.deploy(TokenContract.address); - - await TokenContract.safeMint(addr1.address, "www.google.com") - - }); - - it("contract Addresses", async function() { - - }); + let TokenContract; + let MothershipContract; + //let ProvenanceContract; + let owner; + let addr1; + let addr2; + const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); + const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); + + beforeEach(async function () { + const IDTC = await ethers.getContractFactory("InstrumentDeedToken"); + [owner, addr1, addr2] = await ethers.getSigners(); + TokenContract = await IDTC.deploy(); + + MSC = await ethers.getContractFactory("Mothership"); + MothershipContract = await MSC.deploy(TokenContract.address); + + await TokenContract.safeMint(addr1.address, "www.google.com"); + }); - it("verify token minted to address1", async function() { - expect(await TokenContract.ownerOf(0)).to.equal(addr1.address); - expect(await TokenContract.balanceOf(addr1.address)).to.equal(1); - expect(await TokenContract.tokenURI(0)).to.equal("www.google.com"); - }); + it("contract Addresses", async function () {}); - it("Mothership connected Token Address", async function() { + it("verify token minted to address1", async function () { + expect(await TokenContract.ownerOf(0)).to.equal(addr1.address); + expect(await TokenContract.balanceOf(addr1.address)).to.equal(1); + expect(await TokenContract.tokenURI(0)).to.equal("www.google.com"); + }); - expect(await MothershipContract.instrumentDeedTokenContract()).to.equal(TokenContract.address); - }); + it("Mothership connected Token Address", async function () { + expect(await MothershipContract.instrumentDeedTokenContract()).to.equal( + TokenContract.address + ); + }); - it("deploy Provenance from Mothership", async function() { - //console.log(addr1.address,"addr1.address") - //console.log(MothershipContract.address, "MothershipContract address") - - - const result = await MothershipContract.connect(addr1).createNewProvenance( - 1, - stringToBytes32('serial#'), - stringToBytes32('Selmer'), - stringToBytes32('SBA'), - 1957, - 0, - stringToBytes32("12/31/1999"), - 'ipfs', - ["ipfs"]) - let receipt = await result.wait() - let event = await receipt.events?.filter((x) => {return x.event == "ProvenanceCreated"}); - let ProvenanceAddress = event[0].args.childAddress; - ProvenanceContract = new ethers.Contract(ProvenanceAddress, ProvenanceABI.abi, addr1); - - //Sample Provenance Trait - expect((await ProvenanceContract.instrument()).serial).to.equal(stringToBytes32('serial#')); - //Mothership remains Provenance 'owner' - expect(await ProvenanceContract.owner()).to.equal(MothershipContract.address); - //Provenance listed owner is owner of item - expect((await ProvenanceContract.ownerProvenance(1)).ownerAddress).to.equal(addr1.address) - //expect((await ProvenanceContract.instrumentOwner()).ownerAddress).to.equal(addr1.address); - + it("deploy Provenance from Mothership", async function () { + //console.log(addr1.address,"addr1.address") + //console.log(MothershipContract.address, "MothershipContract address") + + const result = await MothershipContract.connect(addr1).createNewProvenance( + 1, + stringToBytes32("serial#"), + stringToBytes32("Selmer"), + stringToBytes32("SBA"), + 1957, + 0, + stringToBytes32("12/31/1999"), + "ipfs", + ["ipfs"] + ); + let receipt = await result.wait(); + let event = await receipt.events?.filter((x) => { + return x.event == "ProvenanceCreated"; }); + let ProvenanceAddress = event[0].args.childAddress; + ProvenanceContract = new ethers.Contract( + ProvenanceAddress, + ProvenanceABI.abi, + addr1 + ); + + //Sample Provenance Trait + expect((await ProvenanceContract.instrument()).serial).to.equal( + stringToBytes32("serial#") + ); + //Mothership remains Provenance 'owner' + expect(await ProvenanceContract.owner()).to.equal( + MothershipContract.address + ); + //Provenance listed owner is owner of item + expect((await ProvenanceContract.ownerProvenance(1)).ownerAddress).to.equal( + addr1.address + ); + //expect((await ProvenanceContract.instrumentOwner()).ownerAddress).to.equal(addr1.address); + }); - it("sale of from one owner to another", async function() { - let ProvenanceContractTest; - - - const result = await MothershipContract.connect(addr1).createNewProvenance(1, - ethers.utils.formatBytes32String('serial'), - ethers.utils.formatBytes32String('Selmer'), - ethers.utils.formatBytes32String('SBA'), - 1957, 0, - ethers.utils.formatBytes32String("12/31/2000"), - ethers.utils.formatBytes32String('ipfs'), - [ethers.utils.formatBytes32String("ipfs")]) - let receipt = await result.wait() - let event = await receipt.events?.filter((x) => {return x.event == "ProvenanceCreated"}); - let ProvenanceAddress = event[0].args.childAddress; - - - - // prepare transfer - ProvenanceContractTest = new ethers.Contract(ProvenanceAddress, ProvenanceABI.abi, addr1); - - await TokenContract.connect(addr1).approve(ProvenanceContractTest.address, 0) - // console.log(await TokenContract.getApproved(0), "approved for token zero") - await ProvenanceContractTest.connect(addr1).setPendingOwner(addr2.address) - - - // original owner of provenance - let provenanceOwner = (await ProvenanceContractTest.ownerProvenance(1)).ownerAddress; - - - //need to break this into other tests and also make sure that old provenance is removed from the old owner - const ProvenanceContractTestSecondSigner = await ProvenanceContractTest.connect(addr2); - - - await ProvenanceContractTestSecondSigner.connect(addr2).claimOwnership(provenanceOwner, 'verificationPhoto2',stringToBytes32('12/31/1999')); - - - const updatedAddr1Provs = await MothershipContract.connect(addr1).getOwnersInstruments(); - const updatedAddr2Provs = await MothershipContract.connect(addr2).getOwnersInstruments(); - - - - expect(updatedAddr1Provs).to.deep.equal([]) - expect(updatedAddr2Provs[0]).to.equal(ProvenanceContractTest.address) - - expect((await ProvenanceContractTestSecondSigner.ownerProvenance(2)).ownerAddress).to.equal(addr2.address); - expect((await ProvenanceContractTestSecondSigner.ownerProvenance(1)).ownerAddress).to.equal(addr1.address); - - - + it("sale of from one owner to another", async function () { + let ProvenanceContractTest; + + const result = await MothershipContract.connect(addr1).createNewProvenance( + 1, + ethers.utils.formatBytes32String("serial"), + ethers.utils.formatBytes32String("Selmer"), + ethers.utils.formatBytes32String("SBA"), + 1957, + 0, + ethers.utils.formatBytes32String("12/31/2000"), + ethers.utils.formatBytes32String("ipfs"), + [ethers.utils.formatBytes32String("ipfs")] + ); + let receipt = await result.wait(); + let event = await receipt.events?.filter((x) => { + return x.event == "ProvenanceCreated"; }); - + let ProvenanceAddress = event[0].args.childAddress; + + // prepare transfer + ProvenanceContractTest = new ethers.Contract( + ProvenanceAddress, + ProvenanceABI.abi, + addr1 + ); + + await TokenContract.connect(addr1).approve( + ProvenanceContractTest.address, + 0 + ); + // console.log(await TokenContract.getApproved(0), "approved for token zero") + await ProvenanceContractTest.connect(addr1).setPendingOwner(addr2.address); + + // original owner of provenance + let provenanceOwner = (await ProvenanceContractTest.ownerProvenance(1)) + .ownerAddress; + + //need to break this into other tests and also make sure that old provenance is removed from the old owner + const ProvenanceContractTestSecondSigner = + await ProvenanceContractTest.connect(addr2); + + await ProvenanceContractTestSecondSigner.connect(addr2).claimOwnership( + provenanceOwner, + "verificationPhoto2", + stringToBytes32("12/31/1999") + ); + + const updatedAddr1Provs = await MothershipContract.connect( + addr1 + ).getOwnersInstruments(); + const updatedAddr2Provs = await MothershipContract.connect( + addr2 + ).getOwnersInstruments(); + + expect(updatedAddr1Provs).to.deep.equal([]); + expect(updatedAddr2Provs[0]).to.equal(ProvenanceContractTest.address); + + expect( + (await ProvenanceContractTestSecondSigner.ownerProvenance(2)).ownerAddress + ).to.equal(addr2.address); + expect( + (await ProvenanceContractTestSecondSigner.ownerProvenance(1)).ownerAddress + ).to.equal(addr1.address); }); +}); diff --git a/test/SellerTransferTests.js b/test/SellerTransferTests.js index 26d9c5b..2dd3a49 100644 --- a/test/SellerTransferTests.js +++ b/test/SellerTransferTests.js @@ -1,70 +1,76 @@ const { expect } = require("chai"); const { ethers } = require("hardhat"); -const ProvenanceABI = require('../artifacts/contracts/Provenance.sol/Provenance.json'); - +const ProvenanceABI = require("../artifacts/contracts/Provenance.sol/Provenance.json"); //const provider = new ethers.providers.Web3Provider(window.ethereum); //const signer = provider.getSigner(0); describe("Seller Initiated Transfer Tests", function () { - - let TokenContract; - let MothershipContract; - let ProvenanceContractTest; - let owner; - let addr1; - let addr2; - const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); - const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); - - beforeEach(async function () { - const IDTC = await ethers.getContractFactory('InstrumentDeedToken'); - [owner, addr1, addr2] = await ethers.getSigners(); - TokenContract = await IDTC.deploy(); - - MSC = await ethers.getContractFactory('Mothership'); - MothershipContract = await MSC.deploy(TokenContract.address); - - await TokenContract.safeMint(addr1.address, "www.google.com"); - const result = await MothershipContract.connect(addr1).createNewProvenance( - 1, - stringToBytes32('serial#'), - stringToBytes32('Selmer'), - stringToBytes32('SBA'), - 1957, - 0, - stringToBytes32("12/31/1999"), - 'ipfs', - ["ipfs"]) - let receipt = await result.wait() - let event = await receipt.events?.filter((x) => {return x.event == "ProvenanceCreated"}); - let ProvenanceAddress = event[0].args.childAddress; - // prepare transfer - ProvenanceContractTest = new ethers.Contract(ProvenanceAddress, ProvenanceABI.abi, addr1); - await TokenContract.connect(addr1).approve(ProvenanceContractTest.address, 0) - - - - + let TokenContract; + let MothershipContract; + let ProvenanceContractTest; + let owner; + let addr1; + let addr2; + const stringToBytes32 = (string) => ethers.utils.formatBytes32String(string); + const bytes32ToString = (bytes) => ethers.utils.parseBytes32String(bytes); + + beforeEach(async function () { + const IDTC = await ethers.getContractFactory("InstrumentDeedToken"); + [owner, addr1, addr2] = await ethers.getSigners(); + TokenContract = await IDTC.deploy(); + + MSC = await ethers.getContractFactory("Mothership"); + MothershipContract = await MSC.deploy(TokenContract.address); + + await TokenContract.safeMint(addr1.address, "www.google.com"); + const result = await MothershipContract.connect(addr1).createNewProvenance( + 1, + stringToBytes32("serial#"), + stringToBytes32("Selmer"), + stringToBytes32("SBA"), + 1957, + 0, + stringToBytes32("12/31/1999"), + "ipfs", + ["ipfs"] + ); + let receipt = await result.wait(); + let event = await receipt.events?.filter((x) => { + return x.event == "ProvenanceCreated"; }); + let ProvenanceAddress = event[0].args.childAddress; + // prepare transfer + ProvenanceContractTest = new ethers.Contract( + ProvenanceAddress, + ProvenanceABI.abi, + addr1 + ); + await TokenContract.connect(addr1).approve( + ProvenanceContractTest.address, + 0 + ); + }); - it("Provenance Pending Owner is added before transfer and removed after", async function() { - - // console.log(await TokenContract.getApproved(0), "approved for token zero") - await ProvenanceContractTest.connect(addr1).setPendingOwner(addr2.address); - - expect(await ProvenanceContractTest.pendingOwner()).to.equal(addr2.address); - // original owner of provenance - let provenanceOwner = (await ProvenanceContractTest.ownerProvenance(1)).ownerAddress; - //need to break this into other tests and also make sure that old provenance is removed from the old owner - const ProvenanceContractTestSecondSigner = await ProvenanceContractTest.connect(addr2); - await ProvenanceContractTestSecondSigner.connect(addr2).claimOwnership(provenanceOwner, 'verificationPhoto2', stringToBytes32('12/31/1999')) ; - - expect(await ProvenanceContractTest.pendingOwner()).to.equal(ethers.constants.AddressZero); - - }); - - - - + it("Provenance Pending Owner is added before transfer and removed after", async function () { + // console.log(await TokenContract.getApproved(0), "approved for token zero") + await ProvenanceContractTest.connect(addr1).setPendingOwner(addr2.address); + + expect(await ProvenanceContractTest.pendingOwner()).to.equal(addr2.address); + // original owner of provenance + let provenanceOwner = (await ProvenanceContractTest.ownerProvenance(1)) + .ownerAddress; + //need to break this into other tests and also make sure that old provenance is removed from the old owner + const ProvenanceContractTestSecondSigner = + await ProvenanceContractTest.connect(addr2); + await ProvenanceContractTestSecondSigner.connect(addr2).claimOwnership( + provenanceOwner, + "verificationPhoto2", + stringToBytes32("12/31/1999") + ); + + expect(await ProvenanceContractTest.pendingOwner()).to.equal( + ethers.constants.AddressZero + ); }); +});