This repository contains an ERC-7677 proxy template built with Hono that can be spun up by dapps wishing to sponsor user operations originating from smart account users
- Supports Multiple EntryPoints (0.6, 0.7, and 0.8)
- Supporting Multiple Chains at once
- Configure Pimlico sponsorship policies
Go to the Pimlico dashboard and create an API key
Use one of the templates below to deploy an instance of the proxy server, pasting in the previously created Pimlico API key.
On the serverless provider of your choice, create a public URL that will point to your paymaster proxy instance. Use this URL as the paymasterService in your dapp.
1. Copy the .env template and edit it, filling in with your paymaster service provider, chain id whitelist, and more.
cp .env.template .envnpm install
npm run devConsider using Hono helpers such as Bearer Auth, CORS, JWT, Clerk Auth, Next Auth, or any other custom middleware for this. Add this logic to the src/index.ts file.
Your endpoint will now be available at http://localhost:3000/api/paymaster
It is recommended to deploy the endpoint to a hosting provider like Vercel and assign it a custom domain.
Example ERC7677 sponsorship flow using this proxy template running locally:
import { createSmartAccountClient } from "permissionless";
import { http, zeroAddress, createPublicClient } from "viem";
import {
createPaymasterClient,
entryPoint08Address,
} from "viem/account-abstraction";
import { toSimpleSmartAccount } from "permissionless/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { baseSepolia } from "viem/chains";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
// ======== Setting up consts ========
const chain = baseSepolia;
const erc7677ProxyUrl = `http://localhost:3000/api/paymaster`;
const pimlicoUrl = `https://api.pimlico.io/v2/${chain.id}/rpc?apikey=${process.env.PIMLICO_API_KEY}`;
// ======== Setting up clients ========
const publicClient = createPublicClient({
chain,
transport: http(),
});
const pimlicoClient = createPimlicoClient({
transport: http(pimlicoUrl),
});
const paymasterClient = createPaymasterClient({
transport: http(erc7677ProxyUrl),
});
const account = await toSimpleSmartAccount({
client: publicClient,
owner: privateKeyToAccount(generatePrivateKey()), // Random private key
entryPoint: {
address: entryPoint08Address,
version: "0.8",
},
});
const smartAccountClient = createSmartAccountClient({
client: publicClient,
chain,
account: account,
paymaster: paymasterClient,
bundlerTransport: http(pimlicoUrl),
userOperation: {
estimateFeesPerGas: async () => {
const gasPrices = await pimlicoClient.getUserOperationGasPrice();
return gasPrices.fast;
},
},
});
// ======== Sending UserOperation ========
const userOpHash = await smartAccountClient.sendUserOperation({
calls: [
{
to: zeroAddress,
value: 0n,
data: "0x",
},
],
});
const receipt = await smartAccountClient.waitForUserOperationReceipt({
hash: userOpHash,
});
console.log(`UserOp included in tx: ${receipt.receipt?.transactionHash}`);