diff --git a/src/gasless-provider.ts b/src/gasless-provider.ts index 94f5279..a4b4e5a 100644 --- a/src/gasless-provider.ts +++ b/src/gasless-provider.ts @@ -4,10 +4,11 @@ import { EIP1193Provider, RequestArguments } from 'hardhat/types'; import init from 'debug'; import { createPublicClient, concat, encodeFunctionData, Hex } from 'viem'; import { privateKeyToAccount } from 'viem/accounts'; -import { PimlicoBundlerClient, PimlicoPaymasterClient } from 'permissionless/clients/pimlico'; +import { PimlicoBundlerClient } from 'permissionless/clients/pimlico'; import { UserOperation } from 'permissionless/types'; import { getSenderAddress, signUserOperationHashWithECDSA } from 'permissionless'; import * as constants from '../src/constants'; +import { BasePaymaster } from './paymasters'; const log = init('hardhat:plugin:gasless'); @@ -21,7 +22,7 @@ export class GaslessProvider extends ProviderWrapper { protected readonly _wrappedProvider: EIP1193Provider, public readonly chain: string, protected readonly bundlerClient: PimlicoBundlerClient, - protected readonly paymasterClient: PimlicoPaymasterClient, + protected readonly paymasterClient: BasePaymaster, protected readonly publicClient: ReturnType, protected readonly _initCode: `0x${string}`, protected readonly senderAddress: `0x${string}`, @@ -38,7 +39,7 @@ export class GaslessProvider extends ProviderWrapper { _wrappedProvider: EIP1193Provider, chain: string, bundlerClient: PimlicoBundlerClient, - paymasterClient: PimlicoPaymasterClient, + paymasterClient: BasePaymaster, publicClient: ReturnType, ) { // NOTE: Bundlers can support many entry points, but currently they only support one, we use this method so if they ever add a new one the entry point will still work @@ -136,10 +137,7 @@ export class GaslessProvider extends ProviderWrapper { }; // REQUEST PIMLICO VERIFYING PAYMASTER SPONSORSHIP - const sponsorUserOperationResult = await this.paymasterClient.sponsorUserOperation({ - userOperation, - entryPoint: this._entryPoint, - }); + const sponsorUserOperationResult = await this.paymasterClient.sponsorUserOperation(userOperation, this._entryPoint); const sponsoredUserOperation: UserOperation = { ...userOperation, diff --git a/src/index.ts b/src/index.ts index bdbfe10..09d454e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ import { extendProvider } from 'hardhat/config'; import { createPublicClient, http } from 'viem'; -import { createPimlicoPaymasterClient, createPimlicoBundlerClient } from 'permissionless/clients/pimlico'; +import { createPimlicoBundlerClient } from 'permissionless/clients/pimlico'; +import { createPaymasterClient } from './paymaster'; import 'dotenv/config'; @@ -10,6 +11,7 @@ const log = init('hardhat:plugin:gasless'); import './type-extensions'; import { GaslessProvider } from './gasless-provider'; +import { PaymasterType } from './types'; // NOTE: Network name has to match how pimlico names the network in their API calls extendProvider(async (provider, config, networkName) => { @@ -42,9 +44,10 @@ extendProvider(async (provider, config, networkName) => { transport: http(sponsoredTransaction.bundlerUrl), }); - const paymasterClient = createPimlicoPaymasterClient({ - transport: http(sponsoredTransaction.paymasterUrl), - }); + const paymasterClient = createPaymasterClient( + sponsoredTransaction.paymasterType as PaymasterType, + sponsoredTransaction.paymasterUrl, + ); return await GaslessProvider.create(signer, provider, networkName, bundlerClient, paymasterClient, publicClient); }); diff --git a/src/paymaster.ts b/src/paymaster.ts new file mode 100644 index 0000000..69374dd --- /dev/null +++ b/src/paymaster.ts @@ -0,0 +1,13 @@ +import { PaymasterType } from './types'; +import { BasePaymaster } from './paymasters/BasePaymaster'; +import * as Pm from './paymasters'; + +export function createPaymasterClient(paymasterType: PaymasterType, paymasterUrl: string): BasePaymaster { + switch (paymasterType) { + case PaymasterType.Pimlico: + return new Pm.PimlicoPaymaster(paymasterUrl); + + default: + throw new Error(`Unknown paymaster type ${paymasterType}`); + } +} diff --git a/src/paymasters/BasePaymaster.ts b/src/paymasters/BasePaymaster.ts new file mode 100644 index 0000000..e8a31aa --- /dev/null +++ b/src/paymasters/BasePaymaster.ts @@ -0,0 +1,14 @@ +import { PartialUserOperation } from '../types'; + +export class BasePaymaster { + public endpoint: string; + + constructor(endpoint: string) { + this.endpoint = endpoint; + } + + // eslint-disable-next-line + public async sponsorUserOperation(userOp: PartialUserOperation, entryPoint: `0x${string}`): Promise { + throw new Error('This is a base class and should not be called directly.'); + } +} diff --git a/src/paymasters/PimlicoPaymaster.ts b/src/paymasters/PimlicoPaymaster.ts new file mode 100644 index 0000000..6f15b2d --- /dev/null +++ b/src/paymasters/PimlicoPaymaster.ts @@ -0,0 +1,26 @@ +import { BasePaymaster } from './BasePaymaster'; +import { PartialUserOperation } from '../types'; +import { http } from 'viem'; +import { createPimlicoPaymasterClient } from 'permissionless/clients/pimlico'; +import { SponsorUserOperationReturnType } from 'permissionless/actions/pimlico'; + +export class PimlicoPaymaster extends BasePaymaster { + public paymasterClient: ReturnType; + + constructor(endpoint: string) { + super(endpoint); + this.paymasterClient = createPimlicoPaymasterClient({ + transport: http(endpoint), + }); + } + + public async sponsorUserOperation( + userOperation: PartialUserOperation, + entryPoint: `0x${string}`, + ): Promise { + return await this.paymasterClient.sponsorUserOperation({ + userOperation, + entryPoint, + }); + } +} diff --git a/src/paymasters/index.ts b/src/paymasters/index.ts new file mode 100644 index 0000000..3d9004f --- /dev/null +++ b/src/paymasters/index.ts @@ -0,0 +1,2 @@ +export * from './BasePaymaster'; +export * from './PimlicoPaymaster'; diff --git a/src/type-extensions.ts b/src/type-extensions.ts index 5a96617..ef35306 100644 --- a/src/type-extensions.ts +++ b/src/type-extensions.ts @@ -1,11 +1,13 @@ import 'hardhat/types/config'; import 'hardhat/types/runtime'; +import { PaymasterTypeLiteral } from './types'; declare module 'hardhat/types/config' { export interface HttpNetworkUserConfig { sponsoredTransaction?: { bundlerUrl: string; paymasterUrl: string; + paymasterType: PaymasterTypeLiteral; }; } @@ -13,6 +15,7 @@ declare module 'hardhat/types/config' { sponsoredTransaction?: { bundlerUrl: string; paymasterUrl: string; + paymasterType: PaymasterTypeLiteral; }; } } diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..406b823 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,18 @@ +export type PartialUserOperation = { + sender: `0x${string}`; + nonce: bigint; + initCode: `0x${string}`; + callData: `0x${string}`; + maxFeePerGas: bigint; + maxPriorityFeePerGas: bigint; + signature: `0x${string}`; +}; + +export enum PaymasterType { + Pimlico = 'pimlico', + Biconomy = 'biconomy', +} + +export type PaymasterTypeLiteral = keyof { + [K in keyof typeof PaymasterType as string]: K; +}; diff --git a/test/fixture-projects/hardhat-project/hardhat.config.ts b/test/fixture-projects/hardhat-project/hardhat.config.ts index 3a1db5a..ca9dde9 100644 --- a/test/fixture-projects/hardhat-project/hardhat.config.ts +++ b/test/fixture-projects/hardhat-project/hardhat.config.ts @@ -11,6 +11,7 @@ const config: HardhatUserConfig = { sponsoredTransaction: { bundlerUrl: 'http://localhost:3000', paymasterUrl: 'http://localhost:3001', + paymasterType: 'pimlico', }, }, }, diff --git a/test/project.test.ts b/test/project.test.ts index d89fe9e..372a7bd 100644 --- a/test/project.test.ts +++ b/test/project.test.ts @@ -13,5 +13,9 @@ describe('Integration tests examples', function () { it('Should add the paymasterUrl to the config', function () { assert.equal(this.hre.config.networks.localhost.sponsoredTransaction?.paymasterUrl, 'http://localhost:3001'); }); + + it('Should add the paymasterType to the config', function () { + assert.equal(this.hre.config.networks.localhost.sponsoredTransaction?.paymasterType, 'pimlico'); + }); }); });