Skip to content

Commit

Permalink
Merge pull request #193 from pimlicolabs/feat/7677-experimental
Browse files Browse the repository at this point in the history
Add 7677 experimental support
  • Loading branch information
plusminushalf authored May 6, 2024
2 parents 6805c6f + 95f71fe commit 0cb64ff
Show file tree
Hide file tree
Showing 16 changed files with 550 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changeset/mean-otters-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"permissionless": patch
---

Added experimental EIP 7677 support
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{
"workspaces": ["packages/*"],
"workspaces": [
"packages/*"
],
"private": true,
"author": "Pimlico",
"type": "module",
Expand Down
83 changes: 83 additions & 0 deletions packages/permissionless-test/src/ep-0.6/experiments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import {
ENTRYPOINT_ADDRESS_V06,
createSmartAccountClient
} from "permissionless"
import { privateKeyToSafeSmartAccount } from "permissionless/accounts"
import { paymasterActionsEip7677 } from "permissionless/experimental"
import type { ENTRYPOINT_ADDRESS_V06_TYPE } from "permissionless/types"
import { http, createClient, zeroAddress } from "viem"
import { generatePrivateKey } from "viem/accounts"
import { foundry } from "viem/chains"
import { describe, test } from "vitest"
import type { AAParamType } from "../types"
import {
PAYMASTER_RPC,
getPimlicoPaymasterClient,
getPublicClient,
getSafeClient
} from "../utils"

describe.each([
{
name: "Eip 7677 client",

getSmartAccountClient: async (
conf: AAParamType<ENTRYPOINT_ADDRESS_V06_TYPE>
) => getSafeClient(conf)
}
])("$name test", ({ name, getSmartAccountClient }) => {
test("Can get stab data", async () => {
const publicClient = getPublicClient()

const smartAccount = await privateKeyToSafeSmartAccount(publicClient, {
safeVersion: "1.4.1",
entryPoint: ENTRYPOINT_ADDRESS_V06,
privateKey: generatePrivateKey()
})
const ALTO_RPC = "http://localhost:4337"

const paymasterClient = getPimlicoPaymasterClient(
ENTRYPOINT_ADDRESS_V06
)

const smartAccountClient = createSmartAccountClient({
chain: foundry,
account: smartAccount,
bundlerTransport: http(ALTO_RPC)
})

const userOperaton =
await smartAccountClient.prepareUserOperationRequest({
userOperation: {
callData: await smartAccountClient.account.encodeCallData({
to: zeroAddress,
value: 0n,
data: "0x"
})
}
})

const eip7677Client = createClient({
chain: foundry,
transport: http(PAYMASTER_RPC)
}).extend(
paymasterActionsEip7677({ entryPoint: ENTRYPOINT_ADDRESS_V06 })
)

const response = await eip7677Client.getPaymasterData({
userOperation: userOperaton
})

// await eip7677Client.getPaymasterStubData({
// userOperation: await smartClient.prepareUserOperationRequest({
// userOperation: {
// callData: await smartClient.account.encodeCallData({
// to: zeroAddress,
// value: 0n,
// data: "0x"
// })
// }
// })
// })
})
})
31 changes: 21 additions & 10 deletions packages/permissionless-test/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
createPimlicoBundlerClient,
createPimlicoPaymasterClient
} from "permissionless/clients/pimlico"
import { paymasterActionsEip7677 } from "permissionless/experimental"
import type {
ENTRYPOINT_ADDRESS_V06_TYPE,
EntryPoint
Expand All @@ -34,6 +35,7 @@ import {
type Hex,
type Transport,
type WalletClient,
createClient,
createPublicClient,
createWalletClient,
parseEther
Expand All @@ -52,7 +54,7 @@ import type { AAParamType } from "./types"

export const ALTO_RPC = "http://localhost:4337"
const ANVIL_RPC = "http://localhost:8545"
const PAYMASTER_RPC = "http://localhost:3000"
export const PAYMASTER_RPC = "http://localhost:3000"

export const ensureBundlerIsReady = async () => {
const bundlerClient = getBundlerClient(ENTRYPOINT_ADDRESS_V06)
Expand Down Expand Up @@ -212,20 +214,18 @@ export const getLightAccountClient = async <T extends EntryPoint>({
}: AAParamType<T>): Promise<
SmartAccountClient<T, Transport, Chain, SmartAccount<T>>
> => {
const smartAccount = await signerToLightSmartAccount<T, Transport, Chain>(
publicClient,
{
entryPoint,
signer: privateKeyToAccount(privateKey),
lightAccountVersion: "1.1.0"
}
)
const smartAccount = await signerToLightSmartAccount(publicClient, {
entryPoint,
signer: privateKeyToAccount(privateKey),
lightAccountVersion: "1.1.0"
})

// @ts-ignore
return createSmartAccountClient({
chain: foundry,
account: smartAccount,
bundlerTransport: http(ALTO_RPC),
entryPoint: entryPoint,
// eip7677Client: await getEip7677Client({ entryPoint }),
middleware: {
// @ts-ignore
sponsorUserOperation: paymasterClient?.sponsorUserOperation
Expand Down Expand Up @@ -316,3 +316,14 @@ export const getSafeClient = async <T extends EntryPoint>({
}
})
}

export const getEip7677Client = async <TEntryPoint extends EntryPoint>({
entryPoint
}: { entryPoint: TEntryPoint }) => {
const client = createClient({
chain: foundry,
transport: http(PAYMASTER_RPC)
}).extend(paymasterActionsEip7677({ entryPoint }))

return client
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import type {
Transport
} from "viem"
import { getAction } from "viem/utils"
import { type SmartAccount } from "../../accounts/types"
import type { SmartAccount } from "../../accounts/types"
import type { Prettify } from "../../types/"
import type { EntryPoint } from "../../types/entrypoint"
import { AccountOrClientNotFoundError, parseAccount } from "../../utils/"
import { waitForUserOperationReceipt } from "../bundler/waitForUserOperationReceipt"
import { type Middleware } from "./prepareUserOperationRequest"
import type { Middleware } from "./prepareUserOperationRequest"
import { sendUserOperation } from "./sendUserOperation"

export type SendTransactionWithPaymasterParameters<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import type {
Transport
} from "viem"
import { getAction } from "viem/utils"
import { type SmartAccount } from "../../accounts/types"
import type { SmartAccount } from "../../accounts/types"
import type { GetAccountParameter, Prettify } from "../../types/"
import type { EntryPoint } from "../../types/entrypoint"
import { AccountOrClientNotFoundError, parseAccount } from "../../utils/"
import { waitForUserOperationReceipt } from "../bundler/waitForUserOperationReceipt"
import { type Middleware } from "./prepareUserOperationRequest"
import type { Middleware } from "./prepareUserOperationRequest"
import { sendUserOperation } from "./sendUserOperation"

export type SendTransactionsWithPaymasterParameters<
Expand Down
4 changes: 2 additions & 2 deletions packages/permissionless/actions/smartAccount/writeContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import {
encodeFunctionData
} from "viem"
import { getAction } from "viem/utils"
import { type SmartAccount } from "../../accounts/types"
import type { SmartAccount } from "../../accounts/types"
import type { EntryPoint } from "../../types/entrypoint"
import { type Middleware } from "./prepareUserOperationRequest"
import type { Middleware } from "./prepareUserOperationRequest"
import {
type SendTransactionWithPaymasterParameters,
sendTransaction
Expand Down
2 changes: 1 addition & 1 deletion packages/permissionless/clients/decorators/smartAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ export type SmartAccountActions<
TChainOverride
>
>
) => ReturnType<typeof deployContract<entryPoint, TChain, TSmartAccount>>
) => Promise<Hash>
/**
* Executes a write function on a contract.
* This function also allows you to sponsor this transaction if sender is a smartAccount
Expand Down
2 changes: 1 addition & 1 deletion packages/permissionless/errors/sendUserOperation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BaseError } from "viem"
import { type SendUserOperationParameters } from "../actions/bundler/sendUserOperation"
import type { SendUserOperationParameters } from "../actions/bundler/sendUserOperation"
import type { EntryPoint } from "../types/entrypoint"
import { prettyPrint } from "./utils"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import {
type Chain,
ChainNotFoundError,
type Client,
type GetChainParameter,
type Hex,
type Transport,
toHex
} from "viem"
import type {
ENTRYPOINT_ADDRESS_V06_TYPE,
ENTRYPOINT_ADDRESS_V07_TYPE,
EntryPoint,
GetEntryPointVersion
} from "../../../types/entrypoint"
import type {
UserOperation,
UserOperationWithBigIntAsHex
} from "../../../types/userOperation"
import { deepHexlify, getEntryPointVersion } from "../../../utils"
import type {
Eip7677RpcSchema,
GetRpcPaymasterDataReturnType
} from "../types/paymaster"

export type GetPaymasterDataParameters<
TEntryPoint extends EntryPoint,
TChain extends Chain | undefined = Chain | undefined,
TChainOverride extends Chain | undefined = Chain | undefined
> = {
userOperation: UserOperation<GetEntryPointVersion<TEntryPoint>>
entryPoint: TEntryPoint
context?: Record<string, unknown>
} & GetChainParameter<TChain, TChainOverride>

export type GetPaymasterDataReturnType<TEntryPoint extends EntryPoint> =
GetEntryPointVersion<TEntryPoint> extends "v0.6"
? {
paymasterAndData: Hex
}
: {
paymaster: Hex
paymasterData: Hex
}

export async function getPaymasterData<
TEntryPoint extends EntryPoint,
TChain extends Chain | undefined,
TTransport extends Transport = Transport,
TChainOverride extends Chain | undefined = Chain | undefined
>(
client: Client<
TTransport,
TChain,
undefined,
Eip7677RpcSchema<TEntryPoint>
>,
{
userOperation,
entryPoint,
context,
chain
}: GetPaymasterDataParameters<TEntryPoint, TChain, TChainOverride>
): Promise<GetPaymasterDataReturnType<TEntryPoint>> {
const chainId = chain?.id ?? client.chain?.id

if (!chainId) {
throw new ChainNotFoundError()
}

const params:
| [
UserOperationWithBigIntAsHex<GetEntryPointVersion<TEntryPoint>>,
TEntryPoint,
Hex,
Record<string, unknown>
]
| [
UserOperationWithBigIntAsHex<GetEntryPointVersion<TEntryPoint>>,
TEntryPoint,
Hex
] = context
? [
deepHexlify(userOperation) as UserOperationWithBigIntAsHex<
GetEntryPointVersion<TEntryPoint>
>,
entryPoint,
toHex(chainId),
context
]
: [
deepHexlify(userOperation) as UserOperationWithBigIntAsHex<
GetEntryPointVersion<TEntryPoint>
>,
entryPoint,
toHex(chainId)
]

const response = await client.request({
method: "pm_getPaymasterData",
params
})

const entryPointVersion = getEntryPointVersion(entryPoint)

if (entryPointVersion === "v0.6") {
const responseV06 =
response as GetRpcPaymasterDataReturnType<ENTRYPOINT_ADDRESS_V06_TYPE>

return {
paymasterAndData: responseV06.paymasterAndData
} as GetPaymasterDataReturnType<TEntryPoint>
}

const responseV07 =
response as GetRpcPaymasterDataReturnType<ENTRYPOINT_ADDRESS_V07_TYPE>

return {
paymaster: responseV07.paymaster,
paymasterData: responseV07.paymasterData
} as GetPaymasterDataReturnType<TEntryPoint>
}
Loading

0 comments on commit 0cb64ff

Please sign in to comment.