1

我有一个服务器-客户端 DApp,我已经在以太坊测试网络上测试过它可以正常工作。但是由于gas费用,我想使用L2,在这种情况下我选择了Polygon(MATIC)。基本应用程序正在读取和写入网站的文本帖子,智能合约存储它们。

我已经使用 remix.ethereum.org 成功部署在 MATIC 上,并且从 Remix 我可以将交易写入合约。在我的本地主机网络应用程序上,我可以读取交易,但我的写作无法从客户端工作。

这里是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; }
});

这是client.js我添加帖子的片段,通过抓取 html 输入表单然后传递给以下内容:

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);
}

现在这通常可以在以太坊测试网络上完美运行,我所做的只是web3server.js. 但是现在在 MATIC 网络上,在我的客户端浏览器中,

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

这真的让我很困惑,因为

  1. 我可以在 remix.ethereum.org 中手动添加帖子,在那里我部署了完全相同MyContract
  2. 我有其他服务器端调用可以读取MyContract并正常工作(即我可以读取我从 Remix 添加的现有帖子)。

所以我的客户能读不能写,即没有MetaMask弹窗要求我确认支付gas费。

这是我第一次尝试使用 L2,所以我不知道所有web3代码是否应该相同。我一直认为我只需要交换网络并登录到我的 MetaMask 就可以了。但是我对 web3 的理解不是很深,所以我不确定。

非常感谢 - 理想情况下,当我尝试使用 编写时MyContract.methods...(),我应该在客户端浏览器中弹出 MetaMask,要求我确认支付汽油费。

4

1 回答 1

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

该片段从post['addr']地址生成交易,并将生成的交易发送到节点。当 web3 实例持有该地址对应的私钥时,它会在发送前对交易进行签名。

当 web3 不持有私钥时,它会发送未签名的交易。这仅在节点可能持有私钥并仍然签署交易的开发环境(例如 Remix VM 模拟器、Ganache、Hardhat...)中有用。但是在这种情况下,节点没有对应的私钥并返回“未知帐户”错误。


您的问题已经提出了一个解决方案 -请求 MetaMask(或其他持有用户私钥的钱包软件)对交易进行签名,而不是使用 web3 或在节点上对其进行签名。

这需要将事务生成逻辑移动到前端应用程序(在您的情况下),以便前端应用程序可以通过对象(docsclient.js )与可用的 MetaMask API 通信,这在逻辑上在后端应用程序中不可用。window.ethereum

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(),
        gasPrice: '0x09184e72a000', // custom gas price
    };
    // 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();

现在您可以安全地删除端点,因为它不再需要了。

于 2022-02-05T22:23:30.213 回答