Alternative Fee Payments

Objective

Alternative fee payments enable users to interact with an app without holding the network’s primary token (S) to cover gas fees. Instead, gas costs are paid using an alternative method, such as ERC-20 tokens (e.g. stablecoins) or even off-chain settlement. This removes the requirement for users to maintain a balance of the native token.

Solution

An alternative fee payment system can be built on the same ERC-4337 account abstraction principles used in the fee subsidies mechanism.

As with subsidies, users submit UserOperations to a bundler rather than sending transactions directly on-chain. The paymaster contract specified in the UserOperation determines whether to cover the gas cost, typically by interacting with the user’s credit source, most commonly an ERC-20 token.

The paymaster:

  1. Verifies the user has sufficient available credit to cover the estimated gas cost.

  2. Deducts or reserves the required amount from the user’s credit source.

For ERC-20 token payments, the user must first approve an allowance for the paymaster. When assessing a UserOperation, the paymaster transfers the maximum token amount needed to cover gas fees. Once execution is complete and the actual gas usage is known, the paymaster refunds any unused portion to the user.

For popular stablecoins (e.g. USDC), a paymaster is already provided by the token issuer. For other ERC-20 tokens, developers can implement a custom paymaster by extending, for example, PaymasterERC20 from OpenZeppelin community-contracts:

import "@openzeppelin/community-contracts/account/paymaster/PaymasterERC20.sol";

contract MyERC20Paymaster is PaymasterERC20, Ownable {
   function _fetchDetails(
       PackedUserOperation calldata userOp,
       bytes32 /* userOpHash */
   ) internal view virtual override returns (uint256 validationData, IERC20 token, uint256 tokenPrice) {
       // oversimplified example - real-live paymaster should check liveness and sanity of the price
       uint256 price = oracle.getLatestTokenPrice();
       return (
           ERC4337Utils.SIG_VALIDATION_SUCCESS,
           USDC,
           price
       );
   }

   function _authorizeWithdraw() internal virtual override onlyOwner {}
}

A guide for creating similar paymasters can be found in the OpenZeppelin documentation.

Last updated