Skip to content

Commit b133f11

Browse files
committed
Add estimateErc20PaymasterCost
1 parent dff17ee commit b133f11

2 files changed

Lines changed: 177 additions & 0 deletions

File tree

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import {
2+
type Account,
3+
type Address,
4+
type Chain,
5+
ChainNotFoundError,
6+
type Client,
7+
type GetChainParameter,
8+
type Transport,
9+
getAddress,
10+
slice
11+
} from "viem"
12+
import type { EntryPointVersion, UserOperation } from "viem/account-abstraction"
13+
import { getAction } from "viem/utils"
14+
import type { PimlicoRpcSchema } from "../../types/pimlico.js"
15+
import { getTokenQuotes } from "./getTokenQuotes"
16+
17+
/**
18+
* @costInToken represents the max amount of token that will be charged for this user operation in token decimals
19+
* @costInUsd represents the max amount of USD value of the token in 10^6 decimals
20+
*/
21+
export type EstimateErc20PaymasterCostReturnType = {
22+
costInToken: bigint
23+
costInUsd: bigint
24+
}
25+
26+
export type EstimateErc20PaymasterCostParameters<
27+
entryPointVersion extends EntryPointVersion,
28+
TChain extends Chain | undefined,
29+
TChainOverride extends Chain | undefined = Chain | undefined
30+
> = {
31+
entryPoint: { version: entryPointVersion; address: Address }
32+
userOperation: UserOperation<entryPointVersion>
33+
} & GetChainParameter<TChain, TChainOverride>
34+
35+
/**
36+
* Returns all related fields to calculate the potential cost of a userOperation in ERC-20 tokens.
37+
*
38+
* - Docs: https://docs.pimlico.io/permissionless/reference/pimlico-bundler-actions/EstimateErc20PaymasterCost
39+
*
40+
* @param client that you created using viem's createClient whose transport url is pointing to the Pimlico's bundler.
41+
* @returns slow, standard & fast values for maxFeePerGas & maxPriorityFeePerGas
42+
* @returns quotes, see {@link EstimateErc20PaymasterCostReturnType}
43+
*
44+
*/
45+
export const estimateErc20PaymasterCost = async <
46+
entryPointVersion extends EntryPointVersion,
47+
TChain extends Chain | undefined,
48+
TChainOverride extends Chain | undefined = Chain | undefined
49+
>(
50+
client: Client<
51+
Transport,
52+
TChain,
53+
Account | undefined,
54+
PimlicoRpcSchema<entryPointVersion>
55+
>,
56+
args: EstimateErc20PaymasterCostParameters<
57+
entryPointVersion,
58+
TChain,
59+
TChainOverride
60+
>
61+
): Promise<EstimateErc20PaymasterCostReturnType> => {
62+
const chain = args.chain ?? client.chain
63+
64+
if (!chain) {
65+
throw new ChainNotFoundError()
66+
}
67+
68+
const { entryPoint, userOperation } = args
69+
70+
const paymasterData = (() => {
71+
if ("paymasterAndData" in userOperation) {
72+
return userOperation.paymasterAndData
73+
}
74+
75+
if ("paymasterData" in userOperation) {
76+
return userOperation.paymasterData
77+
}
78+
79+
return undefined
80+
})()
81+
82+
if (!paymasterData || paymasterData === "0x") {
83+
throw new Error("Paymaster data not found in the user operation.")
84+
}
85+
86+
const token: Address = (() => {
87+
try {
88+
if (entryPoint.version === "0.6") {
89+
return getAddress(slice(paymasterData, 34, 54))
90+
}
91+
92+
return getAddress(slice(paymasterData, 46, 66))
93+
} catch {
94+
throw new Error(
95+
"Invalid paymaster data, cannot find token address."
96+
)
97+
}
98+
})()
99+
100+
const quotes = await getAction(
101+
client,
102+
getTokenQuotes,
103+
"getTokenQuotes"
104+
)({
105+
tokens: [token],
106+
entryPointAddress: entryPoint.address,
107+
chain
108+
})
109+
110+
const postOpGas: bigint = quotes[0].postOpGas
111+
const exchangeRate: bigint = quotes[0].exchangeRate
112+
const exchangeRateNativeToUsd: bigint = quotes[0].exchangeRateNativeToUsd
113+
114+
const userOperationMaxGas = (() => {
115+
const paymasterVerificationGasLimit =
116+
"paymasterVerificationGasLimit" in userOperation
117+
? (userOperation.paymasterVerificationGasLimit ?? 0n)
118+
: 0n
119+
120+
const paymasterPostOpGasLimit =
121+
"paymasterPostOpGasLimit" in userOperation
122+
? (userOperation.paymasterPostOpGasLimit ?? 0n)
123+
: 0n
124+
125+
const { preVerificationGas, verificationGasLimit, callGasLimit } =
126+
userOperation
127+
128+
return (
129+
BigInt(preVerificationGas) +
130+
BigInt(verificationGasLimit) +
131+
BigInt(callGasLimit) +
132+
paymasterVerificationGasLimit +
133+
paymasterPostOpGasLimit
134+
)
135+
})()
136+
137+
const userOperationMaxCost =
138+
userOperationMaxGas * userOperation.maxFeePerGas
139+
140+
// represents the userOperation's max cost in denomination of wei
141+
const maxCostInWei =
142+
userOperationMaxCost + postOpGas * userOperation.maxFeePerGas
143+
144+
// represents the userOperation's max cost in token denomination (wei)
145+
const costInToken = (maxCostInWei * exchangeRate) / BigInt(1e18)
146+
147+
// represents the userOperation's max cost in usd (with 6 decimals of precision)
148+
const costInUsd = (maxCostInWei * exchangeRateNativeToUsd) / 10n ** 18n
149+
150+
return {
151+
costInToken,
152+
costInUsd
153+
}
154+
}

packages/permissionless/clients/decorators/pimlico.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import {
1010
sendCompressedUserOperation,
1111
validateSponsorshipPolicies
1212
} from "../../actions/pimlico.js"
13+
import {
14+
type EstimateErc20PaymasterCostParameters,
15+
type EstimateErc20PaymasterCostReturnType,
16+
estimateErc20PaymasterCost
17+
} from "../../actions/pimlico/estimateErc20PaymasterCost.js"
1318
import {
1419
type GetUserOperationGasPriceReturnType,
1520
getUserOperationGasPrice
@@ -124,6 +129,18 @@ export type PimlicoActions<
124129
>
125130
>
126131
) => Promise<Prettify<GetTokenQuotesReturnType>>
132+
estimateErc20PaymasterCost: <
133+
TChainOverride extends Chain | undefined = Chain | undefined
134+
>(
135+
args: Omit<
136+
EstimateErc20PaymasterCostParameters<
137+
entryPointVersion,
138+
TChain,
139+
TChainOverride
140+
>,
141+
"entryPoint"
142+
>
143+
) => Promise<Prettify<EstimateErc20PaymasterCostReturnType>>
127144
}
128145

129146
export const pimlicoActions =
@@ -162,5 +179,11 @@ export const pimlicoActions =
162179
...args,
163180
chain: args.chain,
164181
entryPointAddress: entryPoint.address
182+
}),
183+
estimateErc20PaymasterCost: async (args) =>
184+
estimateErc20PaymasterCost(client, {
185+
...args,
186+
entryPoint,
187+
chain: args.chain
165188
})
166189
})

0 commit comments

Comments
 (0)