I’m using minimalforwarder to implement a gas free transaction feature. However if the requested function call reverts, that error doesn’t seem to propagate to the “execute” function call in the forwarder?
For example when I write unit tests in hardhat
await expect(forwarder.execute(req, sig)).to.revert; this test doesn’t pass because it actually does not revert. Even though when I check the state of the contract later, it did not change. Is this expected behavior? Seems like it would be hard to debug if errors in forwarded function calls are silenced.
Code to reproduce
The MinimalForwarder's execute function's signature is:
function execute(ForwardRequest calldata req, bytes calldata signature) public payable returns (bool, bytes memory)
You'll see that the returned values are
bytes where the
bool tells you either the call succeded or not, and
bytes includes the return data (in case the inner call fails, this contains the revert reason).
So yes, in its current format, the minimal forwarder is does not bubble reverts. This is useful is the forwarder is called by a gas repayment mechanism that wan't to refund the payer regardless of the outcome.
Please note that the MinimalForwarder is not a standard/go-to solution, but rather an example of how ERC2771 can be used. We expect developers that are serious about meta-tx relaying to build more sophisticated forwarder for production.
Might be off topic for this post, but what is the use case for this gas repayment mechanism? I thought the whole idea with using forwarder is to have a payment master pay for the gas that the user would have had to pay. But you seem to be saying that there are cases where you want to repay the payment master too? Who is repaying the payment master? Is there a sample project that uses forwarder for gas repayment?
It could be a third party repaying the relayer. It could also be the user repaying using an ERC20, ... there are many possible cases.
I encourage you to read about the GSN (gas station network)