1

Background

I'm working on this app that allows users to upload PDF files to IPFS. A Document consists of the title, address (the uploader), price, description, and the file itself.

Browser

I've looked at other similar questions but none that I found solved my problem.

Problem

Whenever I fill out the information (as shown in the screenshot below), I always encounter this error that occurs in the onSubmit function.

Uncaught Error: invalid string value (arg="file", coderType="string", value=[{"path":"QmUqB9dWDCeZ5nth9YKRJTQ6PcnfrGPPx1vzdyNWV6rh8s","hash":"QmUqB9dWDCeZ5nth9YKRJTQ6PcnfrGPPx1vzdyNWV6rh8s","size":60035}]) at Object.n [as throwError] (web3.min.js:21199)

Code

App.jsx

import React, { Component } from "react";
import MyContract from "./contracts/MyContract.json";
import Web3 from "web3";
import web3 from "./web3";
import ipfs from "./ipfs";
import storehash from "./storehash";

import "./styles/App.css";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      myContractHash: null,
      buffer: "",
      ethAddress: "",
      account: null
    };
  }

  async loadWeb3() {
    if (window.ethereum) {
      window.web3 = new Web3(window.ethereum);
      await window.ethereum.enable();
    } else if (window.web3) {
      window.web3 = new Web3(window.web3.currentProvider);
    } else {
      window.alert("Please switch to a browser that has an Ethereum wallet, such as MetaMask.");
    }
  }

  async loadBlockchain() {
    const web3 = window.web3;
    const accounts = await web3.eth.getAccounts();
    this.setState({ account: accounts[0] });

    const networkId = await web3.eth.net.getId();
    const networkData = MyContract.networks[networkId];

    if (networkData) {
      const contract = new web3.eth.Contract(MyContract.abi, networkData.address);
      this.setState({ contract });

      const myContractHash = await contract.methods.getHash().call();
      this.setState({ myContractHash });
    }
  }

  async componentWillMount() {
    await this.loadWeb3();
    await this.loadBlockchain();
  }
   
  captureFile = (event) => {
    event.stopPropagation();
    event.preventDefault();

    const file = event.target.files[0];

    let reader = new window.FileReader();
    reader.readAsArrayBuffer(file);
    reader.onloadend = () => this.convertToBuffer(reader);
  };

  convertToBuffer = async (reader) => {
    // Convert file to buffer so that it can be uploaded to IPFS
    const buffer = await Buffer.from(reader.result);
    this.setState({buffer});
  };

  onSubmit = async (event) => {
    event.preventDefault();

    // Take the user's MetaMask address
    const accounts = await web3.eth.getAccounts();
    console.log("Sending from MetaMask account: " + accounts[0]);

    // To use the contract's methods
    this.MyContract = new web3.eth.Contract(
      MyContract.abi,
      MyContract.address
    );
    this.MyContract.setProvider(web3.currentProvider);

    // Retrieve the contract address from storehash.js
    const ethAddress= await storehash.options.address;
    this.setState({ethAddress});
    
    // Save document to IPFS, return its hash, and set it to state
    await ipfs.add(this.state.buffer, (err, myContractHash) => {
      console.log(err, myContractHash);
      this.setState({ myContractHash: myContractHash[0].hash });

      this.MyContract.methods
        .publishDocument(
          document.getElementById("title").value,
          document.getElementById("price").value,
          document.getElementById("description").value,
          myContractHash
        )
        .send({ from: this.account, gas: 5000000 });

      console.log("Success!");
    });
  };

  render() {
    return (
      <div>
        <div className="header">
          <a href="/">
            <img src={require("./media/logo.png")} className="logo" />
          </a>
          <div className="header-text">
            <span className="address">{this.state.account}</span>
          </div>
        </div>

        <div className="text-wrapper">
          <p className="heading">Publish a Document</p>
          <form onSubmit={this.onSubmit}>
            <p>Title</p>
            <input type="text" id="title" className="form-input" placeholder="Write title..." />
            <br />
            <p>Price</p>
            <input type="number" id="price" className="form-input" placeholder="0" />
            <br />
            <p>Description</p>
            <input type="textarea" id="description" className="form-input" placeholder="Write description..." />
            <br />
            <p>Choose a document to upload</p>
            <input type="file" onChange={this.captureFile} className="input-file" />

            <button type="submit">Publish</button>
          </form>
        </div>

        <div className="text-wrapper">
          <p className="heading">Feed</p>
        </div>
      </div>
      // <a href={`https://ipfs.infura.io/ipfs/${this.state.myContractHash}`}>Click to download the file</a>
    );
  }
}

export default App;

MyContract.sol

// SPDX-License-Identifier: MIT
pragma solidity >=0.5.16 <0.7.0;

contract MyContract {
  struct Document {
    string title;
    address payable uploader;
    uint256 price;
    string description;
    string file;
  }

  string myContractHash;

  Document[] public documents;

  mapping(address => uint256[]) public filesUploaded;
  mapping(address => uint256[]) public documentsPublished;
  mapping(address => uint256[]) public documentsPurchased;

  // Setters
  function setHash(string memory ipfsHash) public {
    myContractHash = ipfsHash;
  }

  // Getters
  function getHash() public view returns (string memory ipfsHash) {
    return myContractHash;
  }

  function getDocument(uint256 i) public view returns (Document memory) {
    return documents[i];
  }

  function getDocumentsPublishedTotal(address user) public view returns (uint256) {
    return documentsPublished[user].length;
  }

  function getDocumentsPublished(address user, uint256 i) public view returns (uint256) {
    return documentsPublished[user][i];
  }

  function getDocumentsPurchasedTotal(address user) public view returns (uint256) {
    return documentsPurchased[user].length;
  }

  function getDocumentsPurchased(address user, uint256 i) public view returns (uint256) {
    return documentsPurchased[user][i];
  }

  function publishDocument(
    string memory title,
    uint256 price,
    string memory description,
    string memory file
  ) public {
    require(bytes(title).length > 0 && bytes(title).length < 20, "Invalid title length");
    require(bytes(description).length > 0 && bytes(description).length < 10000, "Invalid description length");

    documents.push(Document(title, msg.sender, price, description, file));
    documentsPublished[msg.sender].push(documents.length);
  }

  function isDocumentPurchased(uint256 i) public view returns (bool) {
    for (uint256 j = 0; j < documentsPurchased[msg.sender].length; j++) {
      if (documentsPurchased[msg.sender][j] == i) return true;
    }
    return false;
  }

  function purchaseDocument(uint256 i) setPause public payable {
    require(msg.sender != documents[i].uploader && !isDocumentPurchased(i));

    require(msg.value == documents[i].price, "Invalid price");

    documents[i].uploader.transfer(msg.value);

    documentsPurchased[msg.sender].push(i);
  }
}

How do I solve this? Thank you for your help!

12
  • The message tells you that you are passing input parameter file to contract function publishDocument as a JSON object instead of as a string. – goodvibration Dec 24 '20 at 6:04
  • And BTW, please replace it from a screenshot to plaintext. – goodvibration Dec 24 '20 at 6:04
  • @goodvibration I've edited! – Wolfizzy Dec 24 '20 at 6:07
  • Great, do you understand the problem? – goodvibration Dec 24 '20 at 6:08
  • @goodvibration yes, but I'm not sure how to get it as a string. – Wolfizzy Dec 24 '20 at 6:11
0

Uncaught Error: invalid string value (arg="file", coderType="string", value=[{"path":"QmUqB9dWDCeZ5nth9YKRJTQ6PcnfrGPPx1vzdyNWV6rh8s","hash":"QmUqB9dWDCeZ5nth9YKRJTQ6PcnfrGPPx1vzdyNWV6rh8s","size":60035}]) at Object.n [as throwError] (web3.min.js:21199)

This error-message tells you that you are not passing the input argument file as a string.

And [{"path":"...","hash":"...","size":60035}] is indeed not a string, but an array of objects.

You may want to pass JSON.stringify(x) instead of x for this input argument.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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