2. Build a Paymaster

Step 2 in the "Smart Accounts From Scratch" Series: Let's build and deploy a Paymaster!

⏯️

Want a Video Walkthrough?

Check out this YouTube video of an implementation of this step!

One super nice feature of ERC 4337 Account Abstraction is the ability to have someone else pay for the gas on behalf of the smart account. In this way, the payment for gas can be sponsored or have flexibility in how that payment is done. Let's learn a bit about how the Paymaster works by deploying our own!

Create and Deploy Paymaster Contract

In this step, we'll need to implement the Paymaster interface found here: https://github.com/eth-infinitism/account-abstraction/blob/ver0.6.0/contracts/interfaces/IPaymaster.sol

Let's go ahead and create a very generous paymaster:

import "@account-abstraction/contracts/interfaces/IPaymaster.sol";

contract Paymaster is IPaymaster {
    function validatePaymasterUserOp(UserOperation calldata, bytes32, uint256)
        external
        pure
        override
        returns (bytes memory context, uint256 validationData)
    {
        context = new bytes(0);
        validationData = 0;
    }

    function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external override {}
}

This paymaster considers every user op valid, so it will pay for anything! Thanks paymaster. You can see this in the validatePaymasterUserOp, which is called by the EntryPoint for every user op it receives. We also implemented a postOp but left the function body blank here.

In the validatePaymasterUserOp you'll notice here that we have a couple values we can return here:

  • context - additional context which is supplied to the postOp
  • validationData - a packed set of three variables which is parsed by the entry point here - it checks to see that the first packed value, the sigAuthorizer, is set to 0 and that the block.timestamp falls between the other two values: validAfter and validUntil (zero is a special value indicating a valid signature with no expiration)

Once you have your Paymaster ready, go ahead and deploy it to your local blockchain along with the AccountFactory and EntryPoint from step 1.

:white-check-mark: Task 1/4: Did you deploy the Paymaster to your local blockchain?

Update the PaymasterAndData field

In the last step we left the paymasterAndData field in the user operation as "0x" indicating we were not using a paymaster, and that the smart account is responsible for gas payments. Now that we've deployed our generous paymaster, it's time to use it!

Similar to the initCode field from step 1, the first 20 bytes of the paymasterAndData will be the address of our paymaster. You can see that being parsed by the EntryPoint here: https://github.com/eth-infinitism/account-abstraction/blob/ver0.6.0/contracts/core/EntryPoint.sol#L282-L288

The rest of the data can be parsed by the Paymaster, if it should need it. You'll notice that, in our Paymaster we built above, the first argument to the validate function is a UserOperation. This will be the entire user operation, with the paymasterAndData field included. Its up to the paymaster to parse out the data if its necessary here. In our case, we can forgo the data portion and just leave the paymasterAndData to be the address of the Paymaster you just deployed.

:white-check-mark: Task 2/4: Does your paymasterAndData field start with the paymaster address?

Pre-Fund the Paymaster

In Step 1 we pre-funded the smart account on the EntryPoint. Now, the paymaster is footing the gas bill so we'll need to deposit for them. So now you'll want to deposit some funds for the paymaster to sponsor user operation gas: entryPoint.depositTo(paymasterAddr, { value: 1 ether })

The EntryPoint contract inherits from StakeManager which contains the methods for managing the deposits, you can see that here: https://github.com/eth-infinitism/account-abstraction/blob/ver0.6.0/contracts/core/StakeManager.sol

:white-check-mark: Task 3/4: Was the deposit on behalf of the paymaster successful? You can check with balanceOf method.

Execute the User Operation!

Once again, test it all out by executing the user operation! This time the gas should be covered by the paymaster.

:white-check-mark: Task 4/4: Was your state change initiated through the user operation and was the gas paid for by the paymaster? Was this possible with 0 ether inside of your smart account contract?

If you're able to make a state change on your smart account, just like you did in step 1, you're doing splendid! You've upgraded your smart account to be a gasless smart account :tada:

Next, let's add some validation to ensure our user operations can only be executed by the account owner!