Using MATIC with Web3 and MetaMask: "Error: Returned error: unknown account"
Using MATIC with Web3 and MetaMask: "Error: Returned error: unknown account"
我有一个服务器-客户端 DApp,我测试过它在以太坊测试网络上运行良好。但是由于 gas 费,我想使用 L2,在这种情况下我选择了 Polygon (MATIC)。基本应用程序正在向网站读取和写入文本 posts,智能合约存储它们。
我已经使用 remix.ethereum.org 在 MATIC 上成功部署,并且我可以从 Remix 向合约写入交易。在我的本地主机 Web 应用程序上,我可以读取交易,但我的写入在客户端不起作用。
这是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
中的片段,我在其中添加 post,方法是获取 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);
}
现在这通常可以在以太坊测试网络上完美运行,我所做的只是 server.js
中的 web3
初始化。但是现在在 MATIC 网络上,我在我的客户端浏览器中得到
add-post failed :: Error: Returned error: unknown account
这让我很困惑,因为
- 我可以在 remix.ethereum.org 中手动添加 posts,我在其中部署了完全相同的
MyContract
- 我有其他服务器端调用从
MyContract
读取并且工作正常(即我可以读取现有的 post 我从 Remix 添加)。
所以我的客户只能读但不能写,即没有 MetaMask 弹出窗口要求我确认支付汽油费。
这是我第一次尝试使用 L2,所以我不知道是否所有 web3
代码都应该相同。我的印象是我只需要交换网络并登录到我的 MetaMask,一切都应该没问题。但是我对web3的理解不是很深,所以不确定。
非常感谢 - 理想情况下,当我尝试使用 MyContract.methods...()
编写时,我应该在我的客户端浏览器中看到一个 MetaMask 弹出窗口,要求我确认支付 gas 费用。
MyContract.methods.addNewPost(...).send({from: post['addr'], gas:3000000})
此代码段从post['addr']
地址生成交易,并将生成的交易发送到节点。当 web3 实例持有该地址对应的私钥时,它在发送之前签署交易。
当 web3 不持有私钥时,它会发送未签名的交易。这仅在开发环境(例如 Remix VM 模拟器、Ganache、Hardhat 等)中有用,在该环境中节点可能持有私钥并仍然签署交易。但是在这种情况下,节点没有对应的私钥和returns“未知帐户”错误。
您的问题已经提出了一个解决方案 - 请求 MetaMask(或其他持有用户私钥的钱包软件)签署交易使用 web3 或在节点上对其进行签名。
这需要将交易生成逻辑移动到 前端 应用程序(在您的情况下 client.js
),以便前端应用程序可以与 MetaMask [=36] 通信=] 通过 window.ethereum
对象 (docs) 可用,这在逻辑上在后端应用程序中不可用。
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();
现在您可以安全地删除终结点,因为它不再需要了。
我有一个服务器-客户端 DApp,我测试过它在以太坊测试网络上运行良好。但是由于 gas 费,我想使用 L2,在这种情况下我选择了 Polygon (MATIC)。基本应用程序正在向网站读取和写入文本 posts,智能合约存储它们。
我已经使用 remix.ethereum.org 在 MATIC 上成功部署,并且我可以从 Remix 向合约写入交易。在我的本地主机 Web 应用程序上,我可以读取交易,但我的写入在客户端不起作用。
这是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
中的片段,我在其中添加 post,方法是获取 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);
}
现在这通常可以在以太坊测试网络上完美运行,我所做的只是 server.js
中的 web3
初始化。但是现在在 MATIC 网络上,我在我的客户端浏览器中得到
add-post failed :: Error: Returned error: unknown account
这让我很困惑,因为
- 我可以在 remix.ethereum.org 中手动添加 posts,我在其中部署了完全相同的
MyContract
- 我有其他服务器端调用从
MyContract
读取并且工作正常(即我可以读取现有的 post 我从 Remix 添加)。
所以我的客户只能读但不能写,即没有 MetaMask 弹出窗口要求我确认支付汽油费。
这是我第一次尝试使用 L2,所以我不知道是否所有 web3
代码都应该相同。我的印象是我只需要交换网络并登录到我的 MetaMask,一切都应该没问题。但是我对web3的理解不是很深,所以不确定。
非常感谢 - 理想情况下,当我尝试使用 MyContract.methods...()
编写时,我应该在我的客户端浏览器中看到一个 MetaMask 弹出窗口,要求我确认支付 gas 费用。
MyContract.methods.addNewPost(...).send({from: post['addr'], gas:3000000})
此代码段从post['addr']
地址生成交易,并将生成的交易发送到节点。当 web3 实例持有该地址对应的私钥时,它在发送之前签署交易。
当 web3 不持有私钥时,它会发送未签名的交易。这仅在开发环境(例如 Remix VM 模拟器、Ganache、Hardhat 等)中有用,在该环境中节点可能持有私钥并仍然签署交易。但是在这种情况下,节点没有对应的私钥和returns“未知帐户”错误。
您的问题已经提出了一个解决方案 - 请求 MetaMask(或其他持有用户私钥的钱包软件)签署交易使用 web3 或在节点上对其进行签名。
这需要将交易生成逻辑移动到 前端 应用程序(在您的情况下 client.js
),以便前端应用程序可以与 MetaMask [=36] 通信=] 通过 window.ethereum
对象 (docs) 可用,这在逻辑上在后端应用程序中不可用。
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();
现在您可以安全地删除终结点,因为它不再需要了。