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.
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!
file
to contract functionpublishDocument
as a JSON object instead of as a string. – goodvibration Dec 24 '20 at 6:04