Paymasters with Trampoline - Part I: Token Paymasters

What is a paymaster?

A paymaster is a contract that allows for the payment of fees in an ERC-4337 transaction. This functionality brings about new possibilities in decentralized applications (dapps), similar to how web2 companies pay Visa/Mastercard to process transactions. With Paymasters, users can enjoy blockchain transactions without needing to have Ethereum in their wallet, just as they are accustomed to in web2.

Additionally, paymasters enable users to pay fees using ERC20 tokens such as USDC. By utilizing decentralized oracles, paymasters can fetch the USDC to ETH exchange rate and automatically convert the user's USDC into ETH, allowing for fee payment in a single transaction. This eliminates the need for users to make any extra efforts, enabling them to start transacting on the blockchain without ever owning any ETH.

How do ERC-4337 paymasters work?

ERC4337 Paymaster flow
ERC4337 Paymaster flow

The paymaster functionality in ERC-4337 works as follows:

  1. When a userOperation is sent to the global singleton Entrypoint contract, the first step is to verify the signature from the user's wallet. This ensures that the operation is authenticated.

  2. After the signature verification, if the userOperation specifies a paymaster that should be used, the Entrypoint checks whether that paymaster has sufficient deposited funds to sponsor the gas fees for the operation. If the funds are not enough, the operation is rejected. However, if the funds are sufficient, the userOperation is forwarded to the Paymaster contract for further verification.

  3. The Paymaster contract has the opportunity to review the userOperation and decide whether it wants to sponsor that specific operation or not. For example, in the case of a dapp paymaster, it may check if the userOperation interacts with the dapp's contracts and decide to sponsor it only in such cases. It allows paymasters to have control over which operations they choose to sponsor.

  4. It's important to note that a paymaster is required to stake at the Entrypoint. This stake is locked and serves the purpose of preventing "Sybil attacks" on the blockchain. It makes launching a Denial-of-Service (DoS) attack very expensive.

  5. Once the paymaster has agreed to pay the gas fees, the wallet contract is called to execute the transaction. The execution is completed, and afterwards, the postOp function of the Paymaster contract is called, providing information about the exact gas usage. This allows the paymaster to perform any necessary accounting or further actions if needed.

Paymasters with trampoline

Trampoline is a lightweight framework used for building Smart Contract Wallets based on Chrome extensions. In this article series, we will explain the process of attaching a paymaster to a Smart Contract Wallet built using the Trampoline. We have divided this series into two articles, and in this part, we will explore an example where we attach Pimlico's paymaster. This paymaster will enable your Smart Contract Wallet users to pay gas fees using the USDC token.

By integrating a paymaster into your Smart Contract Wallet, users can conveniently handle gas fees using USDC tokens. This offers a seamless and user-friendly experience when transacting on the blockchain. Doesn’t that sound great? Let’s review the step-by-step process of incorporating Pimlico's paymaster into your Trampoline-based Smart Contract Wallet!

Trampoline’s architecture

To better comprehend the code that follows, it's essential to grasp the basics of Trampoline's architecture. This understanding will enhance your comprehension of the subsequent code snippets.

Trampoline's Component Lifecycle
Trampoline's Component Lifecycle

The transaction process flow within Trampoline can be summarized as follows:

  1. Dapp Initiates Transaction: The dapp triggers a transaction by calling eth_sendTransaction.

  2. Pre-Transaction Confirmation: The PreTransactionConfirmationComponent is loaded. It has the ability to display relevant transaction information or paymaster details and can also return a context.

  3. Unsigned User Operation Creation: The context obtained from the previous step is passed to the background account-api function called createUnsignedUserOpWithContext. This function generates the unsigned user operation, which includes the paymaster and data. If the developer requires specific parameters for the paymaster, they can be included in the context obtained from Step 2, which is then passed to createUnsignedUserOpWithContext.

  4. Transaction Confirmation: The unsigned user operation created in the last step is passed to the TransactionConfirmationComponent. This component serves as the default transaction confirmation screen, but developers have the flexibility to modify it since it is part of the account-api component. The TransactionConfirmationComponent also receives the context from Step 2 and can return a new context if necessary.

  5. Post-Transaction Confirmation: The PostTransactionConfirmationComponent is mounted using the context obtained from Step 4. At this stage, developers can request external signatures after the transaction has been confirmed by the user. For instance, in a two-owner setup, a rainbow wallet’s signature is needed. This component also returns a context, which will be passed to account-api.

  6. User Operation Signature: After Step 5, the account-api is called, and the function signUserOpWithContext is executed, passing the context obtained from Step 5.

  7. User Operation Dispatch

By following this transaction process flow, Trampoline ensures the smooth and secure handling of transactions within the Smart Contract Wallet.

Pimlico’s USDC Paymaster

To make the USDC paymaster by Pimlico work, there are certain steps that need to be followed. While you can refer to Pimlico's documentation for more detailed information about their paymasters and how they function, we will focus on the necessary steps to make the USDC paymaster work with Trampoline.

One crucial requirement is that the wallet must approve access to USDC for Pimlico's Paymaster before it can start sponsoring gas. This step is essential because if you're using SimpleAccount by Eth-infinitism, you need to deploy the wallet and then send a transaction to approve USDC, as outlined in Pimlico's documentation. However, this process requires the user to have access to the native token (ETH) to perform these actions. To overcome this limitation, we will create a new Smart Contract Wallet (SCW) that extends SimpleAccount. In the initialization phase of the SCW, we will approve USDC, allowing us to sponsor Account creation with USDC.

You can find the contract I have written for this purpose at the following GitHub link.

https://github.com/eth-infinitism/trampoline/pull/19/files#diff-02fe0eab7b32550e489ea06dbca1b615be05f3ef9e9a8604d6b04c55b9636987R6
https://github.com/eth-infinitism/trampoline/pull/19/files#diff-02fe0eab7b32550e489ea06dbca1b615be05f3ef9e9a8604d6b04c55b9636987R6

In the provided code, you'll notice that we allow a single transaction to be included in the initialize function call. Here, we will encode the transaction data for approving USDC, which will enable the approval to occur during the creation of the SCW.

We also need to modify the SimpleAccountFactory so that we can call the initialize function with the correct parameters, you can check the code for the factory.

https://github.com/eth-infinitism/trampoline/pull/19/files#diff-6ecb1954fb5cd7b9f02a0663d380367968288a99fccee8696d4ce7478eb9bc97R9
https://github.com/eth-infinitism/trampoline/pull/19/files#diff-6ecb1954fb5cd7b9f02a0663d380367968288a99fccee8696d4ce7478eb9bc97R9

In the factory, as you can see, we now also accept the transaction details that we expect in our new SimpleAccountWithPaymaster.

We must also deploy the account factory before we move forward. To deploy the factory you must make changes to deploy/deploy.ts. You can check out those changes here.

https://github.com/eth-infinitism/trampoline/pull/19/files#diff-0ef065286e7441d1739339474709d9297039ee83c48a8c264eeb5450ee3a132fR7
https://github.com/eth-infinitism/trampoline/pull/19/files#diff-0ef065286e7441d1739339474709d9297039ee83c48a8c264eeb5450ee3a132fR7

Once you have made those changes you can deploy the Factory using npx hardhat deploy –network mumbai. Alternatively, we have already deployed such a factory on the Mumbai network, so you can instead just use 0xb6f4fB799a085ef048c796F22a74B5c646BF77d4 as the factory address in your exconfig. For more info check out the last section about how to test.

IMPORTANT NOTE:

When we approve USDC, the USDC contract accesses global storage to check if USDC is paused or not. This is a problematic limitation because according to the ERC-4337 bundler rules, bundlers cannot allow access to global storage when a SCW is created. To address this, a proposed solution is for well-known bundler providers such as Stackup, Candide, Alchemy, etc., to create a separate mempool that allows ONLY USDC to access global storage. However, for testing purposes, we will be running the bundler locally in an unsafe mode to overcome this limitation, as the alternate mempool is not active yet. You can also choose to use Candide’s Bundler or Pimlico’s Bundler as they have already whitelisted a USDC paymaster.

Approving USDC

Since we have developed our custom Smart Contract Wallet (SCW), we also need to modify the way we generate the initialization code for the account. Referring to the previous section, our SCW's initialize function takes transaction information, and we mentioned that we need to encode the USDC approval transaction information and send it during initialization.

First, we need to initialize the erc20Paymaster provided by Pimlico's SDK.

https://github.com/eth-infinitism/trampoline/pull/19/files#diff-221464352cf87f7853201043c3fd4ced9b2f5620ae8fa71a864d53b298ec5230R55
https://github.com/eth-infinitism/trampoline/pull/19/files#diff-221464352cf87f7853201043c3fd4ced9b2f5620ae8fa71a864d53b298ec5230R55

To modify the generation of the account init code, we will make changes to the getAccountInitCode function in account-api.

https://github.com/eth-infinitism/trampoline/pull/19/files#diff-221464352cf87f7853201043c3fd4ced9b2f5620ae8fa71a864d53b298ec5230R76
https://github.com/eth-infinitism/trampoline/pull/19/files#diff-221464352cf87f7853201043c3fd4ced9b2f5620ae8fa71a864d53b298ec5230R76

In this code snippet, you can observe the following steps:

  1. We retrieve the usdcTokenAddress from Pimlico's erc20Paymaster.

  2. With the usdcTokenAddress, we connect to the ERC20 implementation provided in Pimlico's documentation.

  3. The next step involves obtaining the encoded transaction data for approving the USDC token. In this code, we approve the maximum amount of USDC tokens, but you can adjust this based on your specific use case.

  4. Finally, we wrap this transaction data with our Account's Factory code and return the account init code data.

By incorporating these changes, we ensure that the initialization code for the account includes the necessary steps to approve the USDC token for the needed amount. This enables the proper functioning of the SCW with the integrated USDC paymaster.

Add PaymasterAndData

To continue with the implementation, now that our Smart Contract Wallet (SCW) is ready and we have approved USDC during the creation phase, we move on to the next step. Referring to Trampoline's structure mentioned earlier, we need to return PaymasterAndData in the createUnsignedUserOpWithContext function call that occurs in step 3.

To follow along with the code, you can visit Github here.

https://github.com/eth-infinitism/trampoline/pull/19/files#diff-221464352cf87f7853201043c3fd4ced9b2f5620ae8fa71a864d53b298ec5230R179
https://github.com/eth-infinitism/trampoline/pull/19/files#diff-221464352cf87f7853201043c3fd4ced9b2f5620ae8fa71a864d53b298ec5230R179

In the code snippet, we can observe the following steps:

  1. First, we create an unsigned user operation by calling super.createUnsignedUserOp(info), as we are extending BaseAccountAPI.

  2. Once the unsigned user operation is created, we call adjustGasParameters to handle a small bug in BaseAccountApi where proper gas calculations are not performed.

  3. After adjusting the gas parameters, we invoke erc20Paymaster.generatePaymasterAndData to obtain PaymasterAndData.

  4. Once we have PaymasterAndData, we append it to our user operation and return the unsigned user operation.

With these implementations, our USDC token paymaster code is complete. This enables the integration of USDC as a paymaster within the Trampoline-based Smart Contract Wallet.

How to test it

To test the functionality of the SCW with the integrated USDC paymaster, we need to run the bundler on our local machine, since approving USDC during SCW creation is not supported by current bundler providers in the main mempool. Alternatively, you can use Candide’s Bundler or Pimlico’s Bundler as they have already whitelisted a USDC paymaster.

Here are the steps to follow:

  1. Clone the bundler repository: git clone https://github.com/eth-infinitism/bundler

  2. Install the dependencies by running yarn install.

  3. Compile all local dependencies with yarn preprocess.

  4. Edit bundler.config.json located at packages/bundler/localconfig:

    1. Set the network to your local Hardhat node.

    2. Modify the entryPoint address with the latest address.

    3. Ensure your mnemonic and beneficiary are correctly set up with native tokens in the address corresponding to your mnemonic to forward transactions to the blockchain.

  5. Start the bundler by running yarn bundler --unsafe --auto.

Once the bundler is up and running, you need to make changes to src/exconfig.ts by updating the bundler URL and the factory address for the newly deployed factory. You can find the changes made to exconfig.ts in this link.

In the modified config, the network has been changed to Polygon, the bundler URL is set to Candide’s bundler, and the factory_address is updated with the deployed factory address.

Before proceeding with testing, make sure you obtain some USDC on the Mumbai testnet. If you're using MetaMask, switch your chain to Mumbai and visit this page to swap testnet MATIC to USDC on Uniswap.

Once you have USDC, completed the configuration changes, and have the bundler running, load the Trampoline extension in your Chrome browser and create a new wallet. Transfer at least 10 USDC to the newly created wallet on the Mumbai chain to perform test transactions.

To deploy the account, you can execute any test transaction. For example, in this test, we will call the setGreet function of a greeter contract deployed on the Mumbai network. You can find the contract at https://mumbai.polygonscan.com/address/0xe418F56098e065A15458871F38E136aCd5C55785#code

To perform the test transaction:

  1. Visit PolygonScan and navigate to the "Write Contract" section of the greeter contract.

  2. Click on "Connect to Web3" and grant permission for Trampoline to connect.

  3. Once permission is granted, enter any value in the setGreeting input field and click the button that says "Write".

  4. With that, you will be able to deploy and test a transaction from your SCW using USDC.

By following these steps, you can thoroughly test the SCW with the integrated USDC paymaster, ensuring smooth transaction functionality.

Another type of paymaster which can allow you to pay for gas based on off-chain events is Verying Paymaster. Check out Paymasters with Trampoline - Part II: Verifying Paymasters to know more about it and how to use it with Trampoline.

Subscribe to erc4337
Receive the latest updates directly to your inbox.
Mint this entry as an NFT to add it to your collection.
Verification
This entry has been permanently stored onchain and signed by its creator.