NomicFoundation / hardhat

Hardhat is a development environment to compile, deploy, test, and debug your Ethereum software.
https://hardhat.org
Other
7.3k stars 1.41k forks source link

Delegate call to MUD framework world contract not working on hardhat node #4312

Closed CrazyNorman closed 1 year ago

CrazyNorman commented 1 year ago

Version of Hardhat

2.17.1

What happened?

When I invoke the call function in nodejs to get a value from the world contract like this

const contract = new web3.eth.Contract(worldContractABI, process.env.WORLD_ADDRESS);
const result = await contract.methods.getField(tableId, keys, i).call();

It will raise an error below

AbiError: Parameter decoding error: Returned values aren't valid, did it run Out of Gas? You might also see this error if you are not using the correct ABI for the contract you are retrieving data from, requesting data from a block number that does not exist, or querying a node which is not fully synced.

However when I change to Ganache or anyother test net, everything works fine.

I think this maybe has some relations to the issue: https://github.com/NomicFoundation/hardhat/issues/3798

The getField function is defined here: https://github.com/latticexyz/mud/blob/e9258dae33053eb837f54c4a5d74059cea693f61/packages/store/src/StoreCore.sol#L469

Minimal reproduction steps

Init a mud project

pnpm create mud@canary bug_reproduce

Choose vanilla mode

start a hardhat node (install hardhat dependencies before)

npx hardhat node

Add hardhat node to the packages/contracts/foundry.toml

# add this line below to the foundry.toml
[profile.local]
eth_rpc_url = "http://127.0.0.1:8545"

change the private key in the packages/contracts/.env to the hardhat node funded private key.

cd bug_reproduce/packages/contracts
pnpm run build && mud deploy --profile=local

Get world address from the log like this

{
  worldAddress: '0xB29184E083A14dD8deF236D953574b90fdf2f24E',
  blockNumber: 0
}

Now the contract has been deployed and we can create a new project to test this contract.

mkdir contract_test
cd contract_test
npm install web3
touch test.js

Now paste the code below to the test.js

// test.js
const {
    Web3
} = require('web3');
const web3 = new Web3("http://127.0.0.1:8545");

// Notice: Need to edit to your address and key
const worldAddress = '0xB29184E083A14dD8deF236D953574b90fdf2f24E';
const privateKey = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';

// const web3 = 
const abi = [{
        "inputs": [],
        "name": "increment",
        "outputs": [{
            "internalType": "uint32",
            "name": "",
            "type": "uint32"
        }],
        "stateMutability": "nonpayable",
        "type": "function"
    },
    {
        "inputs": [{
                "internalType": "bytes32",
                "name": "table",
                "type": "bytes32"
            },
            {
                "internalType": "bytes32[]",
                "name": "key",
                "type": "bytes32[]"
            },
            {
                "internalType": "uint8",
                "name": "schemaIndex",
                "type": "uint8"
            }
        ],
        "name": "getField",
        "outputs": [{
            "internalType": "bytes",
            "name": "data",
            "type": "bytes"
        }],
    }

];

const contract = new web3.eth.Contract(abi, worldAddress);
const account = web3.eth.accounts.privateKeyToAccount(privateKey);
const methodKey = "0xd09de08a" // function selector for getField;
const tableId = '0x00000000000000000000000000000000436f756e746572000000000000000000';
console.log(contract.methods);
async function main() {
    const transactionObject = {
        from: account.address,
        to: worldAddress,
        gas: 200000,
        gasPrice: web3.utils.toWei('10', 'gwei'),
        data: contract.methods[methodKey](tableId, [], 0).encodeABI(),
    };

    web3.eth.accounts.signTransaction(transactionObject, privateKey)
        .then(signedTx => {
            web3.eth.sendSignedTransaction(signedTx.rawTransaction)
                .on('receipt', receipt => {
                    console.log(receipt.transactionHash);
                })
                .on('error', error => {
                    console.error(error);
                });
        })
        .catch(error => {
            console.error('Sign failed:', error);
        });

    // Get value
    const increment = await contract.methods[methodKey](tableId, [], 0).call();
    console.log("increment", increment);
}
main();

Now we can run the test

node test.js

We can see the transaction sent successfully, however the .call() raise the error:

AbiError: Parameter decoding error: Returned values aren't valid, did it run Out of Gas? You might also see this error if you are not using the correct ABI for the contract you are retrieving data from, requesting data from a block number that does not exist, or querying a node which is not fully synced.

Now we close the hardhat node, and start a gananche node

ganache

And we deploy the contract again, change the world address and private key in the test.js, then run node test.js again.

Now increment function works fine, no errors .

Search terms

No response

fvictorio commented 1 year ago

Hey @CrazyNorman, sorry for not responding sooner.

I tried to reproduce your example and I did get the same error, but then I make the same thing with ethers like this (it's not exactly the same because I'm not sending the tx, but since your example doesn't await for it then it's kind of the same):

const contract = await ethers.getContractAt(abi, worldAddress);
const value = await contract.getField.staticCall(tableId, [], 0);

and I didn't get an error. So maybe there's something weird going on with web3? That wouldn't explain why it works on ganache though.

fvictorio commented 1 year ago

Closing for lack of response, but will re-open if we get more info.