I decided to try and completely convert my DAPP from web3.js to ethers.js. I got almost everything done until I realised I couldn't actually send a transaction using metamask.
I'm specifically doing this to not use the metamask injected web3 which is soon to be stopped.
let provider = ethers.getDefaultProvider('rinkeby'); // This gives me the read-only access to the network
let contract = new ethers.Contract(contractAddress, abi, provider); // This gives me read-only access to my contract
const metaMask = window.ethereum; // I can connect to my metamask accounts here
What I can't do is:
Send a write method call to my contract using metamask as the signer
A work around would be to use ethers.js to get the payload field for the transaction (Unfortunately I don't have any idea how to do this), then use metamask api to send a normal transaction with the relevant parameters.
You can create a Signer using:
const signer = (new ethers.providers.Web3Provider(window.ethereum)).getSigner()
Which should get you the last bit of the way there, I think? :)
(maybe badly named, but the Web3Provider wraps a Web3 provider (more accurately and EIP-1193 Provider), and returns an ethers Provider :))
Pardon my ignorance but will this be the standard method when metamask stop injecting web3?
I think the final step is getting the method sent to metamask. The original web3.js code that sent the transaction looks like this:
await contract.methods.enter().send({
from: this.state.address,
value: web3.utils.toWei(this.state.value, 'ether')
});
I've converted the value field to ethers.js but I can't see how to send my contract method to metamask
Thanks for the help, hopefully I can get this working tmw.
I think MetaMask just means that they will stop injecting the Web3.js object, and just the EIP-1193 provider as web.ethereum. But if you have more info, please send me a link. :)
To get your contract working, you can either pass the signer into the Contract in the constructor, or use the .connect(signer) method:
const contract = new Contract(address, abi, signer);
// or...
const constant = contract.connect(signer);
Perhaps I have misunderstood because I was already using my own web3 library and they are only stopping providing the web3 library and we can still use them as a provider if we have our own web3 or ethers on the server side?
Yes I already converted to:
const signer = (new ethers.providers.Web3Provider(window.ethereum)).getSigner()
const contract = new ethers.Contract(contractAddress, abi, signer);
But my problem was not knowing how to convert the web3 contract transaction to a ethers version. I've got the transaction working with:
await contract.enter({
value: ethers.utils.parseEther( this.state.value )
});
However it is working differently from web3.js. Previously the await would wait for the transaction confirmation which worked perfectly, now the await only waits until metamask confirms the transaction.
Do I need an additional step of listening for the transaction confirmation?
Sorry for all the questions but it's tough going because almost all tutorials/guides use a web3 implementation and with ethers.js I'm more on my own and the docs are not completely clear.
Ah. This is a known issue with MetaMask and a recent-ish change to how INFURA works, and they have a fix in, but I'm not sure they have pushed it to their Chrome store yet.
The work around right now is to use an UncheckedSigner, which will not pre-populate all the data in the tx object, but allows you to update your UI before requesting the receipt.
The problem comes from ethers returning a transaction object, not simply a hash, so it has to fetch that data from the signer. But MM does not check its inflight tx cache and INFURA no longer returns transactions from the mempool. MM fixed this by checking their inflight tx cache, but it might still take some time for that change to get out in the wild.
I'm also working on a new framework, ethers-app, which aims to help developers around these sorts of issues, but for now, the UncheckedSigner is your best bet. If you are using v5, UncheckedSigner is built-in; you can either use provider.getUncheckedSigner( ... ) or if you have a signer already, you can use signer.connectUnchecked().
I think this has been answered and fixed in MM. I'm going to close this, but if you are still having issues, please re-open.
Thanks! :)
Most helpful comment
You can create a Signer using:
const signer = (new ethers.providers.Web3Provider(window.ethereum)).getSigner()Which should get you the last bit of the way there, I think? :)