简体   繁体   中英

Using MATIC with Web3 and MetaMask: "Error: Returned error: unknown account"

I have a server-client DApp that I've tested working fine on an Ethereum test network. But due to gas fees, I want to use a L2, in this case I chose Polygon (MATIC). Basic app is reading and writing text posts to a website, the smart contract stores them.

I have successfully deployed on MATIC using remix.ethereum.org, and from Remix I can write transactions to the contract. On my localhost web app, I can read transactions, but my writing is not working from the client.

Here is the server.js

const WEB3_PROVIDER = "https://polygon-rpc.com" 
// https://blog.polygon.technology/polygon-rpc-gateway-will-provide-a-free-high-performance-connection-to-the-polygon-pos-blockchain/

//"https://cloudflare-eth.com"; //"HTTP://127.0.0.1:7545"
if (typeof web3 !== 'undefined') {
    web3 = new Web3(web3.currentProvider);
    console.log("web3 already initialized.");
} else {
    // set the provider you want from Web3.providers
    web3 = new Web3(new Web3.providers.HttpProvider(WEB3_PROVIDER));
    console.log("New web3 object initialized.");
}

app.post('/add-post', async (req, res) => {
    const post = req.body;
    
    try {
        console.log(post);

        MyContract.methods.addNewPost(post['name'], post['post'], post['date']).send({from: post['addr'], gas:3000000}).then(function(result) {
            const output_response = "add-post successful :: ";//+String(result);
            res.send(output_response);
        }).catch(function(err) {
            const output_response = "add-post failed :: "+String(err);
            res.send(output_response);
        });
        
    } catch (e) { throw e; }
});

And here is the snippet in client.js where I am adding a post, by grabbing the html input form and then passing to the following:

const web3 = new Web3(window.ethereum);

async function addPost(post_input) {
    stringify_post_input = JSON.stringify(post_input);
    const post_response = await fetch('/add-post', {method: 'POST', body: stringify_post_input, headers: { "content-type": "application/json" } });
    var post_response_text = await post_response.text();
    console.log(post_response_text);
}

Now this usually works flawlessly on ethereum test network, where all I change is the web3 initialization in server.js . But now on the MATIC network I get, in my client browser,

add-post failed :: Error: Returned error: unknown account

This is really confusing to me, because

  1. I can manually add posts in remix.ethereum.org, where I deployed this exact same MyContract
  2. I have other server-side calls that read from MyContract and work fine (ie I can read existing posts I added from Remix).

So my client can read but not write, ie no MetaMask pop-up asking me to confirm to pay gas fees.

This is my first time trying to use a L2, so I have no idea if all the web3 code should be the same. I've been under the impression that I only need to swap the networks and log into my MetaMask and it should all be fine. But I don't really understand web3 that deeply, so I'm not sure.

Help much appreciate - ideally when I try and write with MyContract.methods...() , I should get a MetaMask pop-up in my client browser asking me to confirm paying gas fees.

MyContract.methods.addNewPost(...).send({from: post['addr'], gas:3000000})

This snippet generates a transaction from the post['addr'] address, and sends the generated transaction to the node. When web3 instance holds the corresponding private key to this address, it signs the transaction before sending.

When web3 doesn't hold the private key, it sends the transaction unsigned. Which is useful only in dev environment (eg the Remix VM emulator, Ganache, Hardhat, ...) where the node might hold the private key and still sign the transaction. But in this case, the node doesn't have the corresponding private key and returns the "unknown account" error.


Your question already suggests a solution - request MetaMask (or another wallet software holding the user's private key) to sign the transaction instead of signing it with web3 or on the node.

This requires moving the transaction generating logic to a frontend app (in your case client.js ), so that the frontend app can communicate with the MetaMask API available throuth the window.ethereum object ( docs ), which is logically not available in the backend app.

client.js :

// containing the contract `addPost` function definition
const abiJson = [{
    "inputs": [
        {"internalType": "string", "name": "_name", "type": "string"},
        {"internalType": "string", "name": "_post", "type": "string"},
        {"internalType": "string", "name": "_date", "type": "string"}
    ],
    "name": "addPost", "outputs": [], "stateMutability": "nonpayable", "type": "function"
}];
const contractAddress = "0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF";

let accounts, web3, contract;

async function init() {
    // popup - get the user's address
    accounts = await ethereum.request({ method: 'eth_requestAccounts' });

    // using web3 just as a helper to generate the transaction
    // (see the `data` field and `encodeABI`) - not to sign it
    web3 = new Web3();
    contract = new web3.eth.Contract(abiJson);
}

async function addPost(postInput) {
    const transactionParameters = {
        from: accounts[0],
        to: contractAddress,
        data: contract.methods.addPost(
            postInput.name,
            postInput.post,
            postInput.date
        ).encodeABI(),
    };
    // popup - request the user to sign and broadcast the transaction
    await ethereum.request({
        method: 'eth_sendTransaction',
        params: [transactionParameters],
    });
}

async function run() {
    await init();
    await addPost({
        name: "This is a post name",
        post: "Hello world",
        date: "date"
    });
}

run();

Now you can safely remove the endpoint, as it's not needed anymore.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM