Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions packages/permissionless/actions/pimlico/estimateErc20PaymasterCost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import {
type Account,
type Address,
type Chain,
ChainNotFoundError,
type Client,
type GetChainParameter,
type Transport,
getAddress,
slice
} from "viem"
import type { EntryPointVersion, UserOperation } from "viem/account-abstraction"
import { getAction } from "viem/utils"
import type { PimlicoRpcSchema } from "../../types/pimlico.js"
import { getTokenQuotes } from "./getTokenQuotes"

Check failure on line 15 in packages/permissionless/actions/pimlico/estimateErc20PaymasterCost.ts

View workflow job for this annotation

GitHub Actions / E2E-Coverage

Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './getTokenQuotes.js'?

/**
* @costInToken represents the max amount of token that will be charged for this user operation in token decimals
* @costInUsd represents the max amount of USD value of the token in 10^6 decimals
*/
export type EstimateErc20PaymasterCostReturnType = {
costInToken: bigint
costInUsd: bigint

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if it make more sense to have this as type number

}

export type EstimateErc20PaymasterCostParameters<
entryPointVersion extends EntryPointVersion,
TChain extends Chain | undefined,
TChainOverride extends Chain | undefined = Chain | undefined
> = {
entryPoint: { version: entryPointVersion; address: Address }
userOperation: UserOperation<entryPointVersion>
} & GetChainParameter<TChain, TChainOverride>

/**
* Returns all related fields to calculate the potential cost of a userOperation in ERC-20 tokens.
*
* - Docs: https://docs.pimlico.io/permissionless/reference/pimlico-bundler-actions/EstimateErc20PaymasterCost
*
* @param client that you created using viem's createClient whose transport url is pointing to the Pimlico's bundler.
* @returns slow, standard & fast values for maxFeePerGas & maxPriorityFeePerGas

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this comment can be removed

* @returns quotes, see {@link EstimateErc20PaymasterCostReturnType}
*
*/
export const estimateErc20PaymasterCost = async <
entryPointVersion extends EntryPointVersion,
TChain extends Chain | undefined,
TChainOverride extends Chain | undefined = Chain | undefined
>(
client: Client<
Transport,
TChain,
Account | undefined,
PimlicoRpcSchema<entryPointVersion>
>,
args: EstimateErc20PaymasterCostParameters<
entryPointVersion,
TChain,
TChainOverride
>
): Promise<EstimateErc20PaymasterCostReturnType> => {
const chain = args.chain ?? client.chain

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

const { entryPoint, userOperation } = args

const paymasterData = (() => {
if ("paymasterAndData" in userOperation) {
return userOperation.paymasterAndData
}

if ("paymasterData" in userOperation) {
return userOperation.paymasterData
}

return undefined
})()

if (!paymasterData || paymasterData === "0x") {
throw new Error("Paymaster data not found in the user operation.")
}

const token: Address = (() => {
try {
if (entryPoint.version === "0.6") {
return getAddress(slice(paymasterData, 34, 54))
}

return getAddress(slice(paymasterData, 46, 66))
} catch {
throw new Error(
"Invalid paymaster data, cannot find token address."
)
}
})()

const quotes = await getAction(
client,
getTokenQuotes,
"getTokenQuotes"
)({
tokens: [token],
entryPointAddress: entryPoint.address,
chain
})

const postOpGas: bigint = quotes[0].postOpGas

Check failure on line 110 in packages/permissionless/actions/pimlico/estimateErc20PaymasterCost.ts

View workflow job for this annotation

GitHub Actions / E2E-Coverage

'quotes' is of type 'unknown'.
const exchangeRate: bigint = quotes[0].exchangeRate

Check failure on line 111 in packages/permissionless/actions/pimlico/estimateErc20PaymasterCost.ts

View workflow job for this annotation

GitHub Actions / E2E-Coverage

'quotes' is of type 'unknown'.
const exchangeRateNativeToUsd: bigint = quotes[0].exchangeRateNativeToUsd

Check failure on line 112 in packages/permissionless/actions/pimlico/estimateErc20PaymasterCost.ts

View workflow job for this annotation

GitHub Actions / E2E-Coverage

'quotes' is of type 'unknown'.

const userOperationMaxGas = (() => {
const paymasterVerificationGasLimit =
"paymasterVerificationGasLimit" in userOperation
? (userOperation.paymasterVerificationGasLimit ?? 0n)
: 0n

const paymasterPostOpGasLimit =
"paymasterPostOpGasLimit" in userOperation
? (userOperation.paymasterPostOpGasLimit ?? 0n)
: 0n

const { preVerificationGas, verificationGasLimit, callGasLimit } =
userOperation

return (
BigInt(preVerificationGas) +
BigInt(verificationGasLimit) +
BigInt(callGasLimit) +
paymasterVerificationGasLimit +
paymasterPostOpGasLimit
)
})()

const userOperationMaxCost =
userOperationMaxGas * userOperation.maxFeePerGas

// represents the userOperation's max cost in denomination of wei
const maxCostInWei =
userOperationMaxCost + postOpGas * userOperation.maxFeePerGas

// represents the userOperation's max cost in token denomination (wei)
const costInToken = (maxCostInWei * exchangeRate) / BigInt(1e18)

// represents the userOperation's max cost in usd (with 6 decimals of precision)
const costInUsd = (maxCostInWei * exchangeRateNativeToUsd) / 10n ** 18n

return {
costInToken,
costInUsd
}
}
23 changes: 23 additions & 0 deletions packages/permissionless/clients/decorators/pimlico.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import {
sendCompressedUserOperation,
validateSponsorshipPolicies
} from "../../actions/pimlico.js"
import {
type EstimateErc20PaymasterCostParameters,
type EstimateErc20PaymasterCostReturnType,
estimateErc20PaymasterCost
} from "../../actions/pimlico/estimateErc20PaymasterCost.js"
import {
type GetUserOperationGasPriceReturnType,
getUserOperationGasPrice
Expand Down Expand Up @@ -124,6 +129,18 @@ export type PimlicoActions<
>
>
) => Promise<Prettify<GetTokenQuotesReturnType>>
estimateErc20PaymasterCost: <
TChainOverride extends Chain | undefined = Chain | undefined
>(
args: Omit<
EstimateErc20PaymasterCostParameters<
entryPointVersion,
TChain,
TChainOverride
>,
"entryPoint"
>
) => Promise<Prettify<EstimateErc20PaymasterCostReturnType>>
}

export const pimlicoActions =
Expand Down Expand Up @@ -162,5 +179,11 @@ export const pimlicoActions =
...args,
chain: args.chain,
entryPointAddress: entryPoint.address
}),
estimateErc20PaymasterCost: async (args) =>
estimateErc20PaymasterCost(client, {
...args,
entryPoint,
chain: args.chain
})
})
Loading