From 6f856b09c4a4b50e26ae381a6ee15664d8caaa88 Mon Sep 17 00:00:00 2001 From: excaliborr Date: Wed, 3 Jan 2024 09:00:27 -0500 Subject: [PATCH 1/3] feat: setting up base paymaster and pimlico paymaster --- src/gasless-provider.ts | 10 +++---- src/index.ts | 12 ++++++--- src/paymaster.ts | 13 ++++++++++ src/paymasters/BasePaymaster.ts | 13 ++++++++++ src/paymasters/PimlicoPaymaster.ts | 26 +++++++++++++++++++ src/paymasters/index.ts | 2 ++ src/type-extensions.ts | 3 +++ src/types.ts | 18 +++++++++++++ .../hardhat-project/hardhat.config.ts | 1 + 9 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 src/paymaster.ts create mode 100644 src/paymasters/BasePaymaster.ts create mode 100644 src/paymasters/PimlicoPaymaster.ts create mode 100644 src/paymasters/index.ts create mode 100644 src/types.ts diff --git a/src/gasless-provider.ts b/src/gasless-provider.ts index 94f5279..3143e92 100644 --- a/src/gasless-provider.ts +++ b/src/gasless-provider.ts @@ -8,6 +8,7 @@ import { PimlicoBundlerClient, PimlicoPaymasterClient } from 'permissionless/cli 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..6b49e77 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,11 @@ extendProvider(async (provider, config, networkName) => { transport: http(sponsoredTransaction.bundlerUrl), }); - const paymasterClient = createPimlicoPaymasterClient({ - transport: http(sponsoredTransaction.paymasterUrl), - }); + // TODO: Change this to use our Paymaster classes based on the paymasterType + 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..67cb525 --- /dev/null +++ b/src/paymasters/BasePaymaster.ts @@ -0,0 +1,13 @@ +import { PartialUserOperation } from '../types'; + +export class BasePaymaster { + public endpoint: string; + + constructor(endpoint: string) { + this.endpoint = endpoint; + } + + public async sponsorUserOperation(userOp: PartialUserOperation, arbitraryExtraObject: any | undefined): 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..b4c8d35 --- /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( + userOp: PartialUserOperation, + arbitraryExtraObject: any | undefined, + ): Promise { + return await this.paymasterClient.sponsorUserOperation({ + userOperation: userOp, + entryPoint: arbitraryExtraObject, + }); + } +} 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', }, }, }, From 819a0d75f8ca4d62ca5f670e27b36c3a1e4f0c7b Mon Sep 17 00:00:00 2001 From: excaliborr Date: Wed, 3 Jan 2024 09:11:15 -0500 Subject: [PATCH 2/3] chore: var names --- src/paymasters/BasePaymaster.ts | 2 +- src/paymasters/PimlicoPaymaster.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/paymasters/BasePaymaster.ts b/src/paymasters/BasePaymaster.ts index 67cb525..7c5c890 100644 --- a/src/paymasters/BasePaymaster.ts +++ b/src/paymasters/BasePaymaster.ts @@ -7,7 +7,7 @@ export class BasePaymaster { this.endpoint = endpoint; } - public async sponsorUserOperation(userOp: PartialUserOperation, arbitraryExtraObject: any | undefined): Promise { + 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 index b4c8d35..6f15b2d 100644 --- a/src/paymasters/PimlicoPaymaster.ts +++ b/src/paymasters/PimlicoPaymaster.ts @@ -15,12 +15,12 @@ export class PimlicoPaymaster extends BasePaymaster { } public async sponsorUserOperation( - userOp: PartialUserOperation, - arbitraryExtraObject: any | undefined, + userOperation: PartialUserOperation, + entryPoint: `0x${string}`, ): Promise { return await this.paymasterClient.sponsorUserOperation({ - userOperation: userOp, - entryPoint: arbitraryExtraObject, + userOperation, + entryPoint, }); } } From 605d07a6fd0edd31788fb90e1eddb566ce788348 Mon Sep 17 00:00:00 2001 From: excaliborr Date: Wed, 3 Jan 2024 11:27:55 -0500 Subject: [PATCH 3/3] fix: comments --- src/gasless-provider.ts | 2 +- src/index.ts | 1 - src/paymasters/BasePaymaster.ts | 1 + test/project.test.ts | 4 ++++ 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gasless-provider.ts b/src/gasless-provider.ts index 3143e92..a4b4e5a 100644 --- a/src/gasless-provider.ts +++ b/src/gasless-provider.ts @@ -4,7 +4,7 @@ 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'; diff --git a/src/index.ts b/src/index.ts index 6b49e77..09d454e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -44,7 +44,6 @@ extendProvider(async (provider, config, networkName) => { transport: http(sponsoredTransaction.bundlerUrl), }); - // TODO: Change this to use our Paymaster classes based on the paymasterType const paymasterClient = createPaymasterClient( sponsoredTransaction.paymasterType as PaymasterType, sponsoredTransaction.paymasterUrl, diff --git a/src/paymasters/BasePaymaster.ts b/src/paymasters/BasePaymaster.ts index 7c5c890..e8a31aa 100644 --- a/src/paymasters/BasePaymaster.ts +++ b/src/paymasters/BasePaymaster.ts @@ -7,6 +7,7 @@ export class BasePaymaster { 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/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'); + }); }); });