Moving Forward, save your ETH. Pay gas in ERC20 tokens.

Sachin Tomar
Biconomy
Published in
14 min readMar 25, 2021

--

This is a technical breakdown of how we enable Ethereum gas payments in ERC20 tokens for Biconomy Forward.

The Problem

In the grand scheme of things, ETH is a world away from the minds of the consumer market. Having to hold ETH to interact with Dapps is a completely alien concept.

More than that, it’s a huge inconvenience. Imagine the pain of going through KYC to use an exchange only to have your bank decline the charge; or paying ridiculous amounts of gas on Uniswap, just to top up your ETH reserves.

At Biconomy we’ve realised these pain points users have to go through and developed meta transaction relayer infrastructure that enables developers to pay the gas for their users, as well managing transaction fulfilment (no more stuck transactions).

Biconomy’s gasless meta transactions make on-chain gaming more fun and seamless, L2 DeFi viable and bringing valid use cases to life with more and more non crypto savvy users. However, with gas fees being ridiculously high on L1 there’s no way a Dapp could cover the bill. We work with Curve.fi and their users pay more than $40,000 per day in gas!! (as of 14/01/21)

Fees.wtf has really made people conscious about the opportunity cost of using their ETH to pay gas. However, users on L1 now are seemingly comfortable paying gas fees no matter how much they complain about it.

With ETH prices headed to the moon, paying gas fees with Dai (with a slight markup) makes more sense. Borrowing Dai against your Ether to pay for gas fees, and then using flash loans to repay your debt is much more capital efficient than losing thousands of dollars of lost gains!!

Introducing “Forward”

Solving all of these problems is the goal of our latest offering, Forward : a toolset you can integrate into your dApp to enable your users to be able to pay their gas fees using their ERC20 tokens.

Overview of Forward

Forward is a simple way for your end users to pay gas fees in ERC 20 tokens.

It forwards meta transactions in a secure way, compliant with EIP 2771, and processes gas fee payments in a way which prevents overcharging the end user. We do this by sourcing token price data from transparent on-chain oracles, and charging users based on the amount of gas used for transaction execution.

We’ve also added functionality which will enable Biconomy powered dApps to customise their offering in the future. Including revenue sharing, adding custom tokens, lower fees for regular users, transaction batching etc.

Considering gas optimisation concerns on Mainnet, the fact that certain tokens may lack sufficient liquidity, client requests and edge cases — we’ve decided to begin with a general purpose solution. Our smart contracts could support any token in theory, however, we’re beginning with reliable stablecoins (USDT, USDC, DAI, etc).

Our contracts are open source and on our Github, feel free to take a look.

Fulfilling Forward Meta Transactions

EIP-2771

Secure Protocol for Native Meta Transactions (AKA EIP-2771), is a meta transaction standard proposed collaboratively by a large group of developers in the gasless transaction space — including Open GSN contributors and our CTO Sachin Tomar.

The main idea here is to abstract the verification of meta transaction signatures from the logic of the target contract. A trusted forwarder will handle the verification of the signatures, and append the user address to the call data sent to the target contract.

This gives developers the flexibility to switch out forwarder implementations. Reasons for this may include security/efficiency updates, and supporting new types of signatures that may exist in the future.

Sequence Diagram for EIP-2771

To support EIP-2771 based meta transactions, all developers need to do is inherit BaseRelayRecipient.sol from Open GSN & replace msg.sender with _msgSender().

While Biconomy’s relay infrastructure is currently centralised, we support open source standards and look to decentralise our relay infrastructure at some point in the future.

ERC20ForwardRequest

struct ERC20ForwardRequest {
address from;
address to;
address token;
uint256 txGas;
uint256 tokenGasPrice;
uint256 batchId;
uint256 batchNonce;
uint256 deadline;
bytes data;
}

Every meta transaction going via Forward will be formatted as shown above.

The main points to takeaway include :

The Obvious

If you have experience with Ethereum transactions, you should already be familiar with the from, to, txGas and data fields.

They do the exact same thing as their non meta transaction counterparts : from is the end user’s address, to is the recipient contract’s address, txGas is the gas limit passed for making the recipient contract call, and data is the call data.

Deadlines

Transaction deadlines are designed to protect the end user from slow relayers and changes in market conditions. The transaction will simply revert if it’s being called after the deadline. For example, Uniswap use them to protect users from volatile market conditions. They are also used in USDC’s permit function.

This deadline is a unix timestamp, our SDK will automatically handle this for you. However, if you do NOT want to use it, set it to zero.

Batched Transactions / 2D Nonces

One dimensional nonces prevent the replay of transactions. 2D nonces enable batching of transactions, by assigning a sequence of order dependent transactions the same “batchId”. (Use batchNonce like a standard nonce)

This means you could hypothetically convert WBTC into USDC using Curve.fi & Synthetix, & then deposit into Idle.finance. All of this without having to use a special contract, or having to do each step 1 by 1… Without waiting for those transactions to confirm, you could go to the Metaverse Casino at Decentral.games, immediately — without having your wallet blocked!!

Batches don’t need to be executed in order of ID, further adding flexibility.

While this may not be the most useful feature now, in the near future, we expect batching to become vital. We predict a world where hardware and phone wallets are used for a wide range of daily applications, like e-commerce, identity management and even social media.

Batched meta transactions also serve as a practical alternative to using DSProxy, especially with gas prices spiralling out of control. We see this feature being used as a tool in the dApp developer’s toolkit to speed up development, and improve UX depending on the use case.

However, 2D nonces increase gas usage if used for single transactions, so if you’re only executing a single transaction, we’d recommend not using it and setting batchId to zero (this is the default in our SDK).

Token Specifics

The fields token & tokenGasPrice are used to denote the token that the user will pay in and what gas price to charge (on a token unit level).

To begin with, Biconomy will only support a handful of tokens (see our documentation for more information).

The token gas price will be determined by combining the price feed of the token with our gas price estimates, to calculate a figure denominated in the given token.

For more detailed information, see our section on Charging Users

The Algorithm we use to process Forward meta transactions

BiconomyForwarder (Trusted Forwarder)

(View Contract Code Here)

The trusted forwarder verifies that all aspects of the call, including details of fee payment, have been approved/signed by the message sender. The trusted forwarder then calls the target contract (an Open GSN Base Relay Recipient), appending the message sender data — in a way compliant with EIP-2771.

This contract is intended to be deployed once per network and used by multiple projects (and potentially their customised ERC20Forwarders).

It supports two types of meta transaction signatures : EIP712 & personalSign.

EIP712

EIP712 is an official standard for the display and signing of meta transaction messages.

Endorsed by popular wallets such as Metamask (which even hosted a hackathon encouraging EIP712 usage) and Trust wallet — it aims to serve as a “peer-reviewed well-tested method” for hashing and signing of “complex meaningful messages”.

Read more about EIP 712 here

A key point to note is that EIP712 requires each dApp to have a domain (a struct representing the identity of the verifying contract).

string name,
string version,
address verifyingContract,
bytes32 salt

“salt” here contains the bytes32 representation of chainId value.

Within Forward, BiconomyForwarder is the verifying contract. However, a customisation feature we support is setting your own domain. registerDomainSeparator(string calldata name, string calldata version) can be called by Biconomy to give our clients the ability to “white label” the experience when users go to sign EIP712 messages via Metamask.

PersonalSign

Some wallets may not support EIP712 for your dApp, due to not adhering to the latest standards.

To solve this issue, we created the personalSign solution (also referred to as basicMetaTransaction in our metaTx-standard codebase). It uses a similar encoding to EIP712, however, is designed such that messages can be signed using the eth_sign & personal_sign functions.

ERC20Forwarder

(View Contract Code Here)

The ERC20Forwarder serves an important role holding information needed by Biconomy’s Mexa client SDK. The contract stores the addresses of other contracts that Biconomy’s client libraries need to calculate charges for users before transactions are signed. These include the Fee Manager & Oracle Aggregator. In addition, it stores the fee offsets of using certain tokens (how much extra gas is required for token X) — we refer to this as transferHandlerGas.

User’s gas payments are transferred to a feeReceiver address, this address is set by the owner of the ERC20Forwarder contract deployed. In our current iteration, we operate a single deployment on each network, where Biconomy owns the feeReceiver address.

While Biconomy’s Forward product is currently entirely managed, this design choice opens the door to advanced client customizations in the near future. This may include scenarios where developers use a single ERC20Forwarder for their dApp and build a revenue model around their user experience.

In the meta transaction execution flow, the main purpose of the contract is to initiate the forwarding process and then charge users after the target call is complete. Calling executeEIP712() or executePersonalSign() will call the methods of the same name within our BiconomyForwarder (trusted forwarder). A key point to note is that the ERC20Forwarder contract logs all gas usage required to call BiconomyForwarder.execute__() — this data is used to charge the user.

The BiconomyForwarder will handle the validation and execution of meta transactions. Once the call is run successfully, the ERC20FeeProxy contract will call the transferFrom method on the specified ERC20 token contract address — charging the user’s wallet.

The tokenGasPrice is calculated off-chain, using Biconomy’s recommended gas price and price feed data stored on-chain.

The fee multiplier figure is fetched on-chain from the feeManager during the contract call execution. Again, this maximises developer options in the near future.

Main Points About ERC20Forwarder

  • Stores addresses of contracts needed by Biconomy’s client SDKs
  • Initiates meta transaction forwarding process
  • Charges users after execution is successful
  • FeeReceiver address collects fees charged

Charging Users in ERC20 Tokens

External calls needed to generate a price quote in the Mexa Client SDK

Charging users is a two step process, producing a quote and a tokenGasPrice, and actually charging the user at the end of a transaction. The actual charge and the quote may differ due to the difference between the txGas field, and the real amount of gas used for the call.

TokenGasPrice Formula:

GasPriceInNativeToken * NativeTokenToPaymentTokenPrice

Quote Formula:

TokenGasPrice * FeeMultiplier * ( txGas + transferHandlerGas + additionalOffset )

Real Charge Formula:

TokenGasPrice * FeeMultiplier * ( gasUsedCallingBiconomyForwarder + transferHandlerGas )

All the required data for a quote is fetched by Biconomy’s Mexa SDK. However, only the tokenGasPrice is passed into the meta transaction, as the other elements of the charge are fetched and calculated on-chain.

Biconomy’s Gas Price

To prevent opportunities for erroneous pricing, we ensure the client and Biconomy’s servers have matching gas price data. To see how we select what gas price to use, read this article.

Fee Manager

The fee manager contract is responsible for giving fee multipliers for a given user/token combination — getFeeMultiplier(address user, address token). A fee multiplier essentially serves as a way of setting a markup over the base tokenGasPrice. Biconomy may choose to implement a fee multiplier for a given token to mitigate risk factors, or to generate added revenue.

Biconomy’s default implementation is CentralisedFeeManager.sol, however, any contract implementing IFeeManager.sol can serve as a fee manager. This opens the door to customisation.

A fee multiplier can be between 0 and 6.5535 — expressed as uint16 basis points from 0 to 65535. However, for customisation options in the future, we may impose a minimum >1.

Oracle Aggregator

(View Contract Code Here)

Biconomy intends to support a large range of tokens, over time, on a wide range of networks. Different tokens have varying levels of liquidity and differing tokenomics — this will affect how we source their price data.

Due to security reasons and ease of integration, it makes more sense to source this data from data sources available on-chain.

For popular tokens, such as Dai and USDC, we can use Chainlink’s price feeds. For more niche tokens with AMM liquidity, we can use a Uniswap TWAP price feed, for example. We may need a completely bespoke arrangement for some tokens, for example, where pricing data is sourced from another DeFi protocol.

Interfaces differ with oracle implementations, and it may be preferable to switch oracles being used in the future. Thus, the best solution is to create an adapter, where for each token, we specify what to call and how to call it. Using this approach, we can support any price feed that returns integers (both signed and unsigned).

TransferHandlerGas

The actual charging of users takes up gas, there’s no way to account for it within the call itself, as the charge must be calculated before it is levied. This means we need to add an additional constant to the fee charged.

Additionally, the way in which tokens implement transferFrom() is not consistent, this can be due to factors such as varying levels of efficiency and protocol specific logic. This produces different gas usage figures we must account for.

We add this figure for every token that we support. It can be queried by calling transferHandlerGas(token).

Future Customisation Options

Dedicated ERC20Forwarder

This will be available in the near future

Biconomy aims to eventually give dApp developers the power to roll their own ERC20Forwarder setups — while still benefiting from our infrastructure.

This will give you the ability to do the following :

  • Take fees directly from your users (be the fee receiver)
  • Set custom fee multiplier rules
  • Support any token you want

However, this comes with the risk of needing to repay Biconomy (especially with volatile tokens), and the added responsibility of maintenance for any custom logic implemented on-chain.

Think of this like ejecting in Create React App.

Adding Custom Tokens

In a situation where your dApp is using Forward with a standard setup, adding a custom token would require Biconomy’s approval. We’re currently working on criteria for approving new tokens.

If you’re operating using a dedicated ERC20Forwarder, you could add a new token yourself with little effort. However, you would also need to deploy your own OracleAggregator and FeeManager.

In order to onboard a new token, the following steps need to be completed :

  • Sourcing/development of on-chain price feed
  • Adding of price feed calls to OracleAggregator
  • Setting up of fee manager logic

Building Custom Fee Managers

(This is only possible if you have a dedicated ERC20Forwarder)

There are a handful of reasons to deploy a custom fee manager, a good example is to reward certain users of your dApp/protocol with lower fees.

There are two ways to implement this : deploying a CentralisedFeeManager and continually updating its state; or, making a custom fee manager contract.

CentralisedFeeManager enables developers to set fee multipliers for each user/token combination. This might be ideal if your conditions for varying fees are not very dynamic: e.g you want to reward user X with discounted fees because they bought a special non transferable NFT.

However, if you want to impose more dynamic rules, such as all users holding above 1000 tokens get a discount — it may be a pain to continually update the fee manager. For these kinds of scenarios, we’d recommend making your own fee manager, by implementing IFeeManager.

Gas Usage

Using Forward does add an overhead to your dApps as it requires both validating meta transactions and also charging users at the end of the transaction call.

For this reason, on Ethereum Mainnet, we’d recommend using Forward for high gas transactions, where there is a significant UX benefit to not need Ether.

On layer 2 networks, the costs of using Forward are negligible, making it a no-brainer.

The gas overhead typically varies between 65–75K when using EIP-712 signatures.

We yielded the following overheads for typical scenarios* (with EIP-712) :

  • USDC : 71170
  • USDT : 68603
  • DAI : 65876

However, using personalSign reduces gas usage by an average of 1600 units. The downside is that the in-wallet UX benefits, of using the widely accepted standard, are lost.

*A typical scenario is a non batching transaction with a non-zero batchNonce

Gas Token Integration

Biconomy has developed a technique of using CHI tokens to ease the pain of gas price spikes. We’re offering the same feature with Forward, to lower costs. Using gas tokens enables us to reduce the total overhead of a transaction by up to 50% (including the actual recipient call). Special logic in our contracts enables us to pass this discount onto end users. This means with gas tokens enabled, users could be paying for e.g. 100,000 units instead of 180,000–200,000.

Costed Scenario

The following costs are for a scenario where :

  • Ether price at ATH (currently $1424)
  • Gas Price = 100 Gwei
  • Fee Multiplier of 105%
  • Recipient Call Gas = 200,000 (Imagine a multi path swap or withdrawing lent ERC20 tokens)

Without CHI being used, these transactions costs are typically a third more expensive. However, this is much cheaper than getting users to acquire new Ether and less time consuming. When CHI is enabled, the costs fall $5 below the cost of a traditional transaction. This makes paying with ERC20 tokens using Forward/Biconomy the most practical option for end users, when gas prices are high.

Forward Swap testnet live! — DEX with gas payments in stablecoins

Swap is the first use case built on Biconomy Forward. The decentralized exchange sources its liquidity from Uniswap, but provides the option to pay gas in a variety of stablecoins. This vastly improves the user experience for both crypto newbies & seasoned traders.

Try it out here: forwardswap.io

Conclusion

Forward enables developers to give their users the option to pay for their transactions using ERC20, in a secure and robust manner. We hope this will pave the way for improved UX — with end users never having to hold Ether again to do DeFi with other tokens.

Paying gas in ERC20 tokens will help fight the deflationary economic problem of paying gas in Ether, only to have thousands (or millions) of dollars worth of gains lost forever.

We’re rolling out Forward in a highly managed beta — only supporting a handful of stablecoins. Options that give more flexibility will be released in the near future.

If you want to learn how to integrate Forward in your dApp, please read our documentation.

Special Thanks to

for his contribution to this article.

Check out our website

Follow us on Twitter

Join our gang on telegram

--

--