I am using a ganache local blockchain and truffle to test my smart contracts, this problem started to occur all of a sudden, since before I wasn't running into this problem. So when I call the mintItems function using web3 js I get the EIP1559FeeMarketTransaction.fillFromResult error. To test If my smart contract was working properly I tried running the contract on remix using metamask and ganache (for it to be consistent with web3 implementation) and found it works on remix, so I think this is a problem with my truffle config or js implementation. Can anyone please help
This is the full error message:
ResponseError: Returned error: [ethjs-query] while formatting outputs from RPC '{"value":{"code":-32603,"data":{"message":"VM Exception while processing transaction: revert","stack":"RuntimeError: VM Exception while processing transaction: revert\n at EIP1559FeeMarketTransaction.fillFromResult (C:\\Program Files\\WindowsApps\\GanacheUI_2.7.1.0_x64__rb4352f0jd4m2\\app\\resources\\static\\node\\node_modules\\ganache\\dist\\node\\1.js:2:12745)\n at Miner.<anonymous> (C:\\Program Files\\WindowsApps\\GanacheUI_2.7.1.0_x64__rb4352f0jd4m2\\app\\resources\\static\\node\\node_modules\\ganache\\dist\\node\\1.js:2:36703)\n at async Miner.<anonymous> (C:\\Program Files\\WindowsApps\\GanacheUI_2.7.1.0_x64__rb4352f0jd4m2\\app\\resources\\static\\node\\node_modules\\ganache\\dist\\node\\1.js:2:35116)\n at async Miner.mine (C:\\Program Files\\WindowsApps\\GanacheUI_2.7.1.0_x64__rb4352f0jd4m2\\app\\resources\\static\\node\\node_modules\\ganache\\dist\\node\\1.js:2:39680)\n at async Blockchain.mine (C:\\Program Files\\WindowsApps\\GanacheUI_2.7.1.0_x64__rb4352f0jd4m2\\app\\resources\\static\\node\\node_modules\\ganache\\dist\\node\\1.js:2:60063)\n at async Promise.all (index 0)\n at async TransactionPool.emit (C:\\Program Files\\WindowsApps\\GanacheUI_2.7.1.0_x64__rb4352f0jd4m2\\app\\resources\\static\\node\\node_modules\\ganache\\node_modules\\emittery\\index.js:303:3)","code":-32000,"name":"RuntimeError","data":{"hash":"0x4ea759cfd57e77e10baf6eb1d762f86a19580af73867b4e43e192cff8d369725","programCounter":238,"result":"0x4ea759cfd57e77e10baf6eb1d762f86a19580af73867b4e43e192cff8d369725","reason":null,"message":"revert"}}}}'
at Web3RequestManager.<anonymous> (web3_request_manager.ts:163:1)
at Generator.next (<anonymous>)
at fulfilled (web3_request_manager.ts:1:1)
This is the smart contract I am calling:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol";
import "./Interfaces/ISaleTokens.sol";
contract SaleTokens is ERC1155, ISaleTokens {
constructor() ERC1155("doesn't matter") {}
struct Token {
string _tokenURI;
address _tokenMinter;
}
mapping(uint256 => Token) private _tokens;
uint256 tokenId = 0;
function setTokenURI(uint256 _tokenId, string memory _uri) private {
_tokens[_tokenId]._tokenURI = _uri;
}
function mintItems(uint256 numItems, string memory metadataURI)
public
override
{
bool isNew = true;
uint256 currentTokenId = 0;
for (uint256 i = 1; i <= tokenId; i++) {
if (stringsEquals(_tokens[i]._tokenURI, metadataURI)) {
isNew = false;
currentTokenId = i;
break;
}
}
bytes memory byteMetadata = bytes(metadataURI);
if (isNew || _tokens[currentTokenId]._tokenMinter != msg.sender) {
tokenId++;
currentTokenId = tokenId;
setTokenURI(currentTokenId, metadataURI);
_tokens[currentTokenId]._tokenMinter = msg.sender;
_mint(msg.sender, currentTokenId, numItems, byteMetadata);
} else if (
!isNew && _tokens[currentTokenId]._tokenMinter == msg.sender
) {
setTokenURI(currentTokenId, metadataURI);
_mint(msg.sender, currentTokenId, numItems, byteMetadata);
}
}
function stringsEquals(string memory s1, string memory s2)
private
pure
returns (bool)
{
bytes memory b1 = bytes(s1);
bytes memory b2 = bytes(s2);
uint256 l1 = b1.length;
if (l1 != b2.length) return false;
return keccak256(b1) == keccak256(b2);
}
function transferFrom(
address from,
address to,
uint256 id,
uint256 amount
) public virtual override {
require(
from == _msgSender() || isApprovedForAll(from, _msgSender()),
"ERC1155: caller is not token owner or approved"
);
_safeTransferFrom(from, to, id, amount, "");
}
function editToken(uint256 _tokenId, string calldata newURI) public override {
require(_tokenId <= tokenId && _tokenId > 0, "Invalid token, or token does not exist.");
require(_tokens[_tokenId]._tokenMinter == msg.sender, "Only token minter can edit token.");
require(keccak256(abi.encodePacked(_tokens[_tokenId]._tokenURI)) != keccak256(abi.encodePacked(newURI)), "New URI must be different to oroginal URI");
_tokens[_tokenId]._tokenURI = newURI;
}
function uri(uint256 tokenNum)
public
view
override
returns (string memory)
{
return _tokens[tokenNum]._tokenURI;
}
function getLastTokenId() public view override returns (uint256) {
return tokenId;
}
function burn(uint256 _tokenId, uint256 amount) public override {
_burn(msg.sender, _tokenId, amount);
}
function getMinter(uint256 _tokenId) public view returns (address) {
return _tokens[_tokenId]._tokenMinter;
}
}
This is the js code that handles the web3 transactions
import Web3 from "web3";
import SaleTokenContract from "contracts/SaleTokens.json"
import MarketContract from "contracts/Market.json"
let selectedAccount;
let SaleTokenAddress;
let SaleToken;
let Market;
let MarketAddress;
export const init = async () => {
let provider = window.ethereum;
if(typeof provider !== 'undefined'){
//metamask is installed
provider.request({method: "eth_requestAccounts"})
.then(accounts => {
selectedAccount = accounts[0]
// console.log(`Selected account is ${selectedAccount}`);
})
.catch(err => {
console.log(err);
})
window.ethereum.on('accountsChanged', function(accounts){
selectedAccount = accounts[0]
// console.log(`Selected changed to ${selectedAccount}`);
})
}
const web3 = new Web3(provider);
const networkId = await web3.eth.net.getId();
SaleTokenAddress = SaleTokenContract.networks[networkId].address
MarketAddress = MarketContract.networks[networkId].address
SaleToken = new web3.eth.Contract(SaleTokenContract.abi, SaleTokenAddress)
Market = new web3.eth.Contract(MarketContract.abi, MarketAddress)
};
export const mintItems = (itemNum, tokenURI) => {
return SaleToken.methods.mintItems(itemNum, tokenURI).send({ from: selectedAccount });
}
export const editToken = (tokenNumber, tokenURI) => {
return SaleToken.methods.mintItems(tokenNumber, tokenURI).send({from: selectedAccount })
}
export const approve = (doAllow) => {
return SaleToken.methods.setApprovalForAll(MarketAddress, doAllow).send({ from: selectedAccount });
}
export const getTokenURI = (tokenID) => {
return SaleToken.methods.uri(tokenID).call();
}
export const getTokenCounter = () => {
return SaleToken.methods.getLastTokenId().call();
}
export const getItemMinter = (tokenID) => {
return SaleToken.methods.getMinter(tokenID).call();
}
export const editProductToken = (tokenID, newURI) => {
return SaleToken.methods.editToken(tokenID, newURI).send({ from: selectedAccount });
}
export const listingNum = () =>{
return Market.methods.getNumOfListings().call();
}
export const listingPrice = (_listingId) =>{
return Market.methods.getTokenPrice(_listingId).call();
}
export const listingStock = (_listingId) =>{
return Market.methods.getTokenStock(_listingId).call();
}
export const listingSearchTerms = (_listingId) =>{
return Market.methods.getTokenSearchTerms(_listingId).call();
}
export const getListingTokenURI = (_listingId) =>{
return Market.methods.getListingTokenURI(_listingId).call();
}
export const getListingTokenId = (_listingId) =>{
return Market.methods.getTokenId(_listingId).call();
}
export const getListingTokenSeller = (_listingId) =>{
return Market.methods.getTokenSeller(_listingId).call();
}
export const listToken = (tokenID, price, stock, searchTerms) => {
return Market.methods.listEditProduct(tokenID, stock, price, SaleTokenAddress, searchTerms, 0).send({ from: selectedAccount });
}
export const editListedToken = (tokenID, price, stock, searchTerms, tokenToEdit) => {
return Market.methods.listEditProduct(tokenID, stock, price, SaleTokenAddress, searchTerms, tokenToEdit).send({ from: selectedAccount });
}
export const buyToken = (listingID, amountToBuy, amountToPay) => {
return Market.methods.buyProduct(listingID, amountToBuy).send({ from: selectedAccount, value: amountToPay });
}
And this is my truffle-config
module.exports = {
/**
* Networks define how you connect to your ethereum client and let you set the
* defaults web3 uses to send transactions. If you don't specify one truffle
* will spin up a managed Ganache instance for you on port 9545 when you
* run `develop` or `test`. You can ask a truffle command to use a specific
* network from the command line, e.g
*
* $ truffle test --network <network-name>
*/
networks: {
// Useful for testing. The `development` name is special - truffle uses it by default
// if it's defined here and no other network is specified at the command line.
// You should run a client (like ganache, geth, or parity) in a separate terminal
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 7545, // Standard Ethereum port (default: none)
network_id: "5777", // Any network (default: none)
},
//
// An additional network, but with some advanced options…
// advanced: {
// port: 8777, // Custom port
// network_id: 1342, // Custom network
// gas: 8500000, // Gas sent with each transaction (default: ~6700000)
// gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei)
// from: <address>, // Account to send transactions from (default: accounts[0])
// websocket: true // Enable EventEmitter interface for web3 (default: false)
// },
//
// Useful for deploying to a public network.
// Note: It's important to wrap the provider as a function to ensure truffle uses a new provider every time.
// mumbai: {
// provider: () => new HDWalletProvider(MNEMONIC, `https://polygon-mumbai.infura.io/v3/${PROJECT_ID}`),
// network_id: 80001, // Goerli's id
// confirmations: 2, // # of confirmations to wait between deployments. (default: 0)
// timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50)
// skipDryRun: true // Skip dry run before migrations? (default: false for public nets )
// },
//
// Useful for private networks
// private: {
// provider: () => new HDWalletProvider(MNEMONIC, `https://network.io`),
// network_id: 2111, // This network is yours, in the cloud.
// production: true // Treats this network as if it was a public net. (default: false)
// }
},
// Set default mocha options here, use special reporters, etc.
mocha: {
// timeout: 100000
},
// Configure your compilers
compilers: {
solc: {
version: "0.8.18" // Fetch exact version from solc-bin (default: truffle's version)
// docker: true, // Use "0.5.1" you've installed locally with docker (default: false)
// settings: { // See the solidity docs for advice about optimization and evmVersion
// optimizer: {
// enabled: false,
// runs: 200
// },
// evmVersion: "byzantium"
// }
}
}
// Truffle DB is currently disabled by default; to enable it, change enabled:
// false to enabled: true. The default storage location can also be
// overridden by specifying the adapter settings, as shown in the commented code below.
//
// NOTE: It is not possible to migrate your contracts to truffle DB and you should
// make a backup of your artifacts to a safe location before enabling this feature.
//
// After you backed up your artifacts you can utilize db by running migrate as follows:
// $ truffle migrate --reset --compile-all
//
// db: {
// enabled: false,
// host: "127.0.0.1",
// adapter: {
// name: "indexeddb",
// settings: {
// directory: ".db"
// }
// }
// }
};