This past week Metamask introduced a new function called "wallet_addEthereumChain" which allows the user to automatically add a new Etheruem RPC to their wallet when prompted. This function also allows the user to change the network they are connected to, for example, if I already have Binance Smart Chain connected to metamask, calling wallet_addEthereumChain changes the active network to BSC. However, when trying this for the Ethereum chain, it gives an error that you cannot add mainnet RPC.
I have used the following code to change to change from Ethereum Mainnet to Binance Smart Chain, and it works fine:
switchToBinance: async function () {
let ethereum = window.ethereum;
const data = [{
chainId: '0x38',
chainName: 'Binance Smart Chain',
nativeCurrency:
{
name: 'BNB',
symbol: 'BNB',
decimals: 18
},
rpcUrls: ['https://bsc-dataseed.binance.org/'],
blockExplorerUrls: ['https://bscscan.com/'],
}]
/* eslint-disable */
const tx = await ethereum.request({method: 'wallet_addEthereumChain', params:data}).catch()
if (tx) {
console.log(tx)
}
},
However when I try the Exact thing, metamask throws an exception saying I cannot add a mainnet RPC:
switchToEthereum: async function () {
let ethereum = window.ethereum;
const data = [{
chainId: '0x1',
chainName: 'Ethereum',
nativeCurrency: {
name: 'Ethereum',
symbol: 'ETH',
decimals: 18,
},
rpcUrls: ['https://mainnet.infura.io/v3/undefined'],
blockExplorerUrls: ['https://etherscan.io'],
}]
/* eslint-disable */
const tx = await ethereum.request({method: 'wallet_addEthereumChain', params:data}).catch()
if (tx) {
console.log(tx)
}
},
However, the request for adding a new RPC connection, and changing the active RPC connection is the same. So is there a way to change the active Ethereum provider from a custom chain to Mainnet (chain ID-1)
as this issue comment point, for security reason wallet_addEthereumChain not support mainnet. But there a new EIP to resolve this issue, follow EIP-3326 to find release info, and this discuss to see draft progrss.
chainId: '0x38' // error because await not decimal
Number('0x38').toString(10) //56 - chain ID of BSC
Right:
chainId: `0x${Number(56).toString(16)}`
OR:
chainId: `0x86`
Simply you have to prefix 0x with the chainId.
Example:
await ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: '0x1' }],
})
Related
I have this sample code:
ethereum
.request({
method: 'eth_sendTransaction',
params: [
{
from: accounts[0],
to: '0x2f318C334780961FB129D2a6c30D0763d9a5C970',
value: '0x29a2241af62c0000',
gasPrice: '0x09184e72a000',
gas: '0x2710',
},
],
})
.then((txHash) => console.log(txHash))
.catch((error) => console.error);
I have BSC addresses, so when I try to make transaction, I can send only BNB. How can I transfer another coin?
In order to send coins rather than BNB, you have to find a contract address that has "Transfer" Function to transfer the coin.
For example, if you want to transfer USDC on BSC. You can use web3.js or ethers.js to connect "0x0a385f86059e0b2a048171d78afd1f38558121f3" (this address) and use the Transfer function to USDC.
const tempProvider = new ethers.providers.Web3Provider(window.ethereum);
const tempSigner = tempProvider.getSigner();
const tempContract = new ethers.Contract("0x0a385f86059e0b2a048171d78afd1f38558121f3", abi, tempSigner);
tempContract.transfer(to, amount);
something like this
It seems the example code on the MetaMask documentation page, sends ETH only. How should I customize the sample code to send some custom tokens?
const transactionParameters = {
nonce: '0x00', // ignored by MetaMask
gasPrice: '0x09184e72a000', // customizable by user during MetaMask confirmation.
gas: '0x2710', // customizable by user during MetaMask confirmation.
to: '0x0000000000000000000000000000000000000000', // Required except during contract publications.
from: ethereum.selectedAddress, // must match user's active address.
value: '0x00', // Only required to send ether to the recipient from the initiating external account.
data:
'0x7f7465737432000000000000000000000000000000000000000000000000000000600057', // Optional, but used for defining smart contract creation and interaction.
chainId: '0x3', // Used to prevent transaction reuse across blockchains. Auto-filled by MetaMask.
};
// txHash is a hex string
// As with any RPC call, it may throw an error
const txHash = await ethereum.request({
method: 'eth_sendTransaction',
params: [transactionParameters],
});
A transaction sending an ERC-20 token needs to have the token contract as the recipient (the to field), and the data field containing encoded instructions to execute its transfer() function along with address of the token receiver and amount.
const transactionParameters = {
from: accounts[0],
to: tokenContractAddress,
data: getDataFieldValue(tokenRecipientAddress, tokenAmount),
};
await ethereum.request({
method: 'eth_sendTransaction',
params: [transactionParameters],
});
You can use for example the web3js library (docs) to encode the data field value. The transfer() function is standardized - so assuming the token contract follows the standard, it's going to be the same for any token contract.
function getDataFieldValue(tokenRecipientAddress, tokenAmount) {
const web3 = new Web3();
const TRANSFER_FUNCTION_ABI = {"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"};
return web3.eth.abi.encodeFunctionCall(TRANSFER_FUNCTION_ABI, [
tokenRecipientAddress,
tokenAmount
]);
}
For a cleaner version of Petr's solution using the encodeABI function.
await window.ethereum
.request({
method: "eth_sendTransaction",
params: [
{
from: accounts[0],
to: TOKEN_CONTRACT_ADDRESS,
data: tokenContract.methods
.transfer(receiverAddress, amount)
.encodeABI(),
},
],
})
.then((result) => console.log(result))
.catch((error) => console.error(error));
I need a very simple way to sign data with Metamask and Web3. I am very familiar with using eth account sign with Web3, but I want to incorporate Metamask into it. I have read the Metamask docs on signing data, but the examples they gave are outdated.
The one thing I have done: Successfully enabled Ethereum and got access to the user's address who connected.
Any suggestions on a very very simple way to sign data? I'm really just testing things out Metamask and want to get started.
const getSignedData = async () => {
const messageToSign = "any message you create or fetch";
const accounts = (await ethereum?.request({
method: "eth_requestAccounts",
})) as string[];
// account will be the signer of this message
const account = accounts[0];
// password is the third param as uuid
const signedData = await ethereum?.request({
method: "personal_sign",
params: [
JSON.stringify(messageToSign.data),
account,
messageToSign.data.id,
],
});
return { signedData, account };
};
I'm using hardhat locally and have a react frontend up and running but I can't call the methods without errors.
I've tried both ethers.js and web3.
Here's my code and attempts. Please let me know if you see what I'm doing wrong.
I'm trying to interact with contracts that are deployed in the local hardhat env through web3
I'm unable to get back the data from the contract, here's the info
I have:
var list = await contract.methods.getList();
console.log("list ", list );
which gets me
list {arguments: Array(0), call: ƒ, send: ƒ, encodeABI: ƒ, estimateGas: ƒ, …}
When I do
var list = await contract.methods.getList().call();
console.log("list ", list );
I get this error in the browser:
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.
I do:
Setup in console:
npx hardhat node
>Started HTTP and WebSocket JSON-RPC server at http://127.0.0.1:8545/
>Accounts
>========
>...
npx hardhat compile
> Nothing to compile
npx hardhat run scripts/deploy.js --network hardhat
Note: In the deploy.js file, I do a
const list = await contract.getList();
console.log("list", list ); // correctly outputs ["string", "string"]
The method:
mapping(uint256 => address) internal list;
uint256 internal listCount;
function getList() public override view returns (address[] memory) {
address[] memory assets = new address[](listCount);
for (uint256 i = 0; i < listCount; i++) {
assets[i] = list[i];
}
return assets;
}
In react App.js:
import Contract_from './data/abi/Contract_.json'; // Contract_ is a placer
var contract = new web3.eth.Contract(Contract_, address_given_on_deploy);
var contractAddress = await contract .options.address; // correctly outputs
var list= await contract.methods.getList().call();
console.log("list", list);
As you see, this doesn't return the values from the method. What am I doing wrong here?
For any reason and may be likely the issue, here's my config:
require("#nomiclabs/hardhat-waffle");
// openzeppelin adds
require("#nomiclabs/hardhat-ethers");
require('#openzeppelin/hardhat-upgrades');
//abi
require('hardhat-abi-exporter');
// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async () => {
const accounts = await ethers.getSigners();
for (const account of accounts) {
console.log(account.address);
}
});
// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more
/**
* #type import('hardhat/config').HardhatUserConfig
*/
module.exports = {
networks: {
hardhat: {
gas: 12000000,
blockGasLimit: 0x1fffffffffffff,
allowUnlimitedContractSize: true,
timeout: 1800000,
chainId: 1337
}
},
solidity: {
compilers: [
{
version: "0.8.0",
settings: {
optimizer: {
enabled: true,
runs: 1000
}
}
},
{
version: "0.8.2",
settings: {
optimizer: {
enabled: true,
runs: 1000
}
}
},
],
},
abiExporter: {
path: './frontend/src/data/abi',
clear: true,
flat: true,
only: [],
spacing: 2
}
}
__
I thought maybe i would try ethers.js since that is what i do my testing in but same issue.
For whatever reason, I can "get" the contracts, print the methods that belong to them, but I can't actually call the methods.
Here's my ethers.js brevity:
provider = new ethers.providers.Web3Provider(window.ethereum);
if(provider != null){
const _contract = new ethers.Contract(address, _Contract, provider);
var list= await _contract.getList().call();
console.log("list", list);
}
The error i get from this is:
Error: call revert exception (method="getList()", errorArgs=null, errorName=null, errorSignature=null, reason=null, code=CALL_EXCEPTION, version=abi/5.4.0)
I've tried numerous contracts in the protocol and same thing for each
Using truffle (3.4.6), metamask (3.9.2) and testrpc (4.0.1), I call a transaction and get "Error: the tx doesn't have the correct nonce. account has nonce of: 5 tx has nonce of: 15". I have cut down the body of my contract method to something trivial and I still get this. Any idea what causes it?
contract MyContract {
mapping (address => bool) authorized;
function myMethod (uint element, uint price) whenNotPaused returns (bool) {
if (!authorized[msg.sender]) throw;
return true;
}
}
I call the method like this (using truffle):
MyContract.deployed().then((instance) => {
instance.myMethod (id, price, {from: account}).then (...)
In this thread i saw a suggestion of switching networks back and forth and the error self correcting.
¯\_(ツ)_/¯
#okwme is right, but you can also change your configuration of the network to fix the issue. Assume you're running a dev network on localhost with a HDWallet provider, then you can fix the error by commenting out that config property and add host and port properties to it.
It looks like follows:
development: {
host: "127.0.0.1",
port: 8545,
network_id: "*",
gas: 6721975
// networkCheckTimeout: 10000,
// provider: function() {
// return new HDWalletProvider(mnemonic, 'http://127.0.0.1:8545/', 0, 10);
// },
// network_id: '*',
// gas: 6721975,
},
},