1

I was working on a NFT marketplace app using NextJS as frontend and Solidity as backend. When I tried to sell an NFT using the frontend UI, I encountered this error:

Access to XMLHttpRequest at 'https://gateway.pinata.cloud/ipfs/QmbbWLfoPg9aSpFCKoYQRadQynmCRMjydVhkXJZKBXKnyT' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Here is my code:

import {useState} from 'react';
import {ethers} from 'ethers';
import {useRouter} from 'next/router';
import Web3Modal from 'web3modal';
import {contractAddress, INFURA_URL, PINATA_KEY, PINATA_SECRET} from '../config.js';
import NFTMarketplace from "../abi/NFTMarketplace.json";
import axios from 'axios';
import Image from 'next/image';

export default function createNFT() {
    const [fileURL, setFileURL] = useState(null);
    const [formInput, updateFormInput] = useState({price: "", name: "", description: ""});
    const router = useRouter();
    const [loadingState, setLoadingState] = useState('Not loaded');

    // upload image to IPFS
    async function imageUpload(e) {
        const file = e.target.files[0];
        try {
            const formData = new FormData();
            formData.append("file", file);
            const resFile = await axios({
                method: "post",
                url: "https://api.pinata.cloud/pinning/pinFileToIPFS",
                data: formData,
                headers: {
                    'pinata_api_key': PINATA_KEY,
                    'pinata_secret_api_key': PINATA_SECRET,
                    'Content-Type': 'multipart/form-data'
                }
            });

            const imageURL = `https://gateway.pinata.cloud/ipfs/${
                resFile.data.IpfsHash
            }`;
            setFileURL(imageURL)

        } catch (e) {
            console.log(e)
        }
    }

    // upload metadata to IPFS and return URL to use in later transaction
    async function uploadToIPFS() {
        const {name, description, price} = formInput;
        if (!name || !description || !price || !fileURL) {
            return
        }
        setLoadingState('Loading...')

        try {
            let jsonData = JSON.stringify({
                "pinataMetadata": {
                    "name": `${
                        name.json
                    }`
                },
                "pinataContent": {
                    name,
                    description,
                    image: fileURL
                }
            })

            const resFile = await axios({
                method: "post",
                url: "https://api.pinata.cloud/pinning/pinJSONToIPFS",
                data: jsonData,
                headers: {
                    'pinata_api_key': PINATA_KEY,
                    'pinata_secret_api_key': PINATA_SECRET,
                    'Content-Type': 'application/json'
                }
            });

            const tokenURI = `https://gateway.pinata.cloud/ipfs/${
                resFile.data.IpfsHash
            }`;
            return tokenURI;
        } catch (error) {
            console.log("Error uploading file: ", error);
        }
    }

    async function listNFTForSale() {
        const tokenURI = await uploadToIPFS();
        const web3modal = new Web3Modal();
        const connection = await web3modal.connect();
        const provider = new ethers.providers.Web3Provider(connection);
        const getnetwork = await provider.getNetwork();
        const goerliChainId = 5;
        if (getnetwork.chainId != goerliChainId) {
            alert("You are not connected to the Goerli network!")
            return;
        }
        // sign the transaction
        const signer = provider.getSigner();
        const contract = new ethers.Contract(contractAddress, NFTMarketplace.abi, signer);
        const price = ethers.utils.parseUnits(formInput.price, 'ether');
        let listingPrice = await contract.getListingPrice();
        listingPrice = listingPrice.toString();
        let transaction = await contract.createToken(tokenURI, price, {value: listingPrice});
        await transaction.wait();
        router.push('/');
    }
    return (<div className='flex justify-center'>
        <div className='w-1/8 flex-col mr-10 mt-10'> {
            !fileURL && (<Image className='rounded mt-4' src='/image_place_holder.jpg' alt="Image placeholder" width={300} height={200}/>)
        }
            {
            fileURL && (<Image src={fileURL} alt="Image uploaded successfully" className='rounded mt-4' placeholder="blur" blurDataURL="/image_place_holder.jpg" width={300} height={200}/>)
        }</div>
        <div className='w-1/2 flex flex-col'>
            <input placeholder='Asset Name' className='mt-8 border rounded p-4' onChange={e=>updateFormInput({...formInput, name: e.target.value})}/>
            <textarea placeholder='Asset Description' className='mt-2 border rounded p-4' onChange={e=>updateFormInput({...formInput, description: e.target.value})}/>
            <input placeholder='Asset Price in Ethers' className='mt-2 border rounded p-4' type="number" onChange={e=>updateFormInput({...formInput, price: e.target.value})}/>
            <input type="file" name="Asset" className='my-4' onChange={imageUpload} /> {
            fileURL && (<button onClick={listNFTForSale} className="font-bold mt-4 bg-pink-500 text-white rounded p-4 shadow-lg"> {
                loadingState == 'Not loaded' ? 'Create NFT' : 'Uploading...'
            } </button>)
        } </div>
    </div>)
}

enter image description here enter image description here enter image description here

I tried installing the Access-Control-Allow-Origin extension on Chrome, but it doesn't work. Other approaches suggested usually work with ExpressJS and NodeJS but I'm working with NextJS. How do I resolve this? Let me know if you need other details. Thank you!

2 Answers 2

1

For productive use, libraries mentioned by @NoName can be used. Another alternative is nextjs-cors.

For local development; however, the plugin should suffice. Just make sure that the plugin is turned on. This needs to be done as an additional step to installing the plugin.

0

From here, you need to add this header to all pinata requests:

headers: {
    'Accept': 'text/plain'
  }

in try block you should have this

      const formData = new FormData();
      formData.append("file", file);

      const resFile = await axios({
          method: "post",
          maxBodyLength: Infinity,
          url: "https://api.pinata.cloud/pinning/pinFileToIPFS",
          data: formData,
          headers: {
                'pinata_api_key': PINATA_KEY,
                'pinata_secret_api_key': PINATA_SECRET,
                // 'Content-Type': 'multipart/form-data'
                "Content-Type": `multipart/form-data: boundary=${formData.getBoundary()}`,
                Accept: "text/plain",
            }
      });

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.