abstractionkit 0.2.41 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1103,11 +1103,14 @@ var Bundler = class {
1103
1103
  state_override_set
1104
1104
  ]);
1105
1105
  const res = jsonRpcResult;
1106
- return {
1106
+ const gasEstimationResult = {
1107
1107
  callGasLimit: BigInt(res.callGasLimit),
1108
1108
  preVerificationGas: BigInt(res.preVerificationGas),
1109
1109
  verificationGasLimit: BigInt(res.verificationGasLimit)
1110
1110
  };
1111
+ if (res.paymasterVerificationGasLimit != null) gasEstimationResult.paymasterVerificationGasLimit = BigInt(res.paymasterVerificationGasLimit);
1112
+ if (res.paymasterPostOpGasLimit != null) gasEstimationResult.paymasterPostOpGasLimit = BigInt(res.paymasterPostOpGasLimit);
1113
+ return gasEstimationResult;
1111
1114
  } catch (err) {
1112
1115
  throw new AbstractionKitError("BUNDLER_ERROR", "bundler eth_estimateUserOperationGas rpc call failed", { cause: ensureError(err) });
1113
1116
  }
@@ -3792,7 +3795,7 @@ var SafeAccount = class SafeAccount extends SmartAccount {
3792
3795
  * @param toolVersion - tool version, defaults to current abstractionkit version
3793
3796
  * @returns the onchain idenetifier as a hex string (not 0x prefixed)
3794
3797
  */
3795
- function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.2.41") {
3798
+ function generateOnChainIdentifier(project, platform = "Web", tool = "abstractionkit", toolVersion = "0.3.1") {
3796
3799
  const identifierPrefix = "5afe";
3797
3800
  const identifierVersion = "00";
3798
3801
  const projectHash = keccak256("0x" + Buffer.from(project, "utf8").toString("hex")).slice(-20);
@@ -4180,36 +4183,39 @@ var SafeMultiChainSigAccountV1 = class SafeMultiChainSigAccountV1 extends SafeAc
4180
4183
  * @param overrides - overrides for the default values
4181
4184
  * @returns signature
4182
4185
  */
4183
- static formatSignaturesToUseroperationsSignatures(userOperationsToSign, signerSignaturePairs, overrides = {}) {
4186
+ static formatSignaturesToUseroperationsSignatures(userOperationsToSign, signerSignaturePairs) {
4184
4187
  if (userOperationsToSign.length < 1) throw new RangeError("There should be at least one userOperationsToSign");
4185
- const resolvedOverrides = {
4188
+ const defaultOverrides = {
4186
4189
  eip7212WebAuthnPrecompileVerifier: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_PRECOMPILE,
4187
4190
  eip7212WebAuthnContractVerifier: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_DAIMO_VERIFIER,
4188
4191
  webAuthnSignerFactory: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SIGNER_FACTORY,
4189
4192
  webAuthnSignerSingleton: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SIGNER_SINGLETON,
4190
4193
  webAuthnSignerProxyCreationCode: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SIGNER_PROXY_CREATION_CODE,
4191
4194
  safe4337ModuleAddress: SafeMultiChainSigAccountV1.DEFAULT_SAFE_4337_MODULE_ADDRESS,
4192
- webAuthnSharedSigner: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SHARED_SIGNER,
4193
- ...overrides
4195
+ webAuthnSharedSigner: SafeMultiChainSigAccountV1.DEFAULT_WEB_AUTHN_SHARED_SIGNER
4194
4196
  };
4195
4197
  if (userOperationsToSign.length === 1) return [SafeAccount.formatSignaturesToUseroperationSignature(signerSignaturePairs, {
4196
- ...resolvedOverrides,
4198
+ ...defaultOverrides,
4199
+ ...userOperationsToSign[0].overrides,
4200
+ validAfter: userOperationsToSign[0].validAfter,
4201
+ validUntil: userOperationsToSign[0].validUntil,
4197
4202
  isMultiChainSignature: true
4198
4203
  })];
4199
4204
  const userOperationsHashes = [];
4200
- userOperationsToSign.forEach((userOperationsToSign, _index) => {
4201
- const userOperationHash = SafeAccount.getUserOperationEip712Hash_V9(userOperationsToSign.userOperation, userOperationsToSign.chainId, {
4202
- validAfter: userOperationsToSign.validAfter,
4203
- validUntil: userOperationsToSign.validUntil,
4204
- safe4337ModuleAddress: resolvedOverrides.safe4337ModuleAddress
4205
+ userOperationsToSign.forEach((userOperationToSign, _index) => {
4206
+ const userOperationHash = SafeAccount.getUserOperationEip712Hash_V9(userOperationToSign.userOperation, userOperationToSign.chainId, {
4207
+ validAfter: userOperationToSign.validAfter,
4208
+ validUntil: userOperationToSign.validUntil,
4209
+ safe4337ModuleAddress: userOperationToSign.overrides?.safe4337ModuleAddress ?? defaultOverrides.safe4337ModuleAddress
4205
4210
  });
4206
4211
  userOperationsHashes.push(userOperationHash);
4207
4212
  });
4208
4213
  const [_root, proofs] = generateMerkleProofs(userOperationsHashes);
4209
4214
  const userOpSignatures = [];
4210
- userOperationsToSign.forEach((_userOperationsToSign, index) => {
4215
+ userOperationsToSign.forEach((userOperationToSign, index) => {
4211
4216
  userOpSignatures.push(SafeAccount.formatSignaturesToUseroperationSignature(signerSignaturePairs, {
4212
- ...resolvedOverrides,
4217
+ ...defaultOverrides,
4218
+ ...userOperationToSign.overrides,
4213
4219
  isMultiChainSignature: true,
4214
4220
  multiChainMerkleProof: proofs[index]
4215
4221
  }));
@@ -6511,9 +6517,9 @@ var Paymaster = class {};
6511
6517
  /** Buffer added to verificationGasLimit for paymasterAndData verification overhead */
6512
6518
  const PAYMASTER_V06_VERIFICATION_OVERHEAD = 40000n;
6513
6519
  /** Max value for uint256 */
6514
- const UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935n;
6520
+ const UINT256_MAX$1 = 115792089237316195423570985008687907853269984665640564039457584007913129639935n;
6515
6521
  /** Multiplier for token approve amount to cover paymasterAndData cost variance */
6516
- const TOKEN_APPROVE_AMOUNT_MULTIPLIER = 2n;
6522
+ const TOKEN_APPROVE_AMOUNT_MULTIPLIER$1 = 2n;
6517
6523
  /**
6518
6524
  * ERC-20 tokens that require resetting their allowance to 0 before setting a
6519
6525
  * new approval amount (e.g. USDT on mainnet).
@@ -6523,7 +6529,7 @@ const TOKEN_APPROVE_AMOUNT_MULTIPLIER = 2n;
6523
6529
  * please open an issue at https://github.com/candidelabs/abstractionkit/issues
6524
6530
  * or use the `resetApproval` override as a workaround.
6525
6531
  */
6526
- const TOKENS_REQUIRING_ALLOWANCE_RESET = ["0xdac17f958d2ee523a2206206994597c13d831ec7"];
6532
+ const TOKENS_REQUIRING_ALLOWANCE_RESET$1 = ["0xdac17f958d2ee523a2206206994597c13d831ec7"];
6527
6533
  /**
6528
6534
  * Client for the Candide Paymaster service.
6529
6535
  * Supports both gas sponsorship (sponsor paymaster) and ERC-20 token payment for gas (token paymaster).
@@ -6863,12 +6869,12 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
6863
6869
  if (epData == null) throw new RangeError(`UserOperation for entrypoint ${entrypoint} is not supported`);
6864
6870
  this.setDummyPaymasterFields(userOp, epData);
6865
6871
  const oldCallData = userOp.callData;
6866
- const requiresAllowanceReset = overrides?.resetApproval ?? TOKENS_REQUIRING_ALLOWANCE_RESET.includes(context.token.toLowerCase());
6867
- let callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(userOp.callData, context.token, epData.paymasterMetadata.address, UINT256_MAX);
6872
+ const requiresAllowanceReset = overrides?.resetApproval ?? TOKENS_REQUIRING_ALLOWANCE_RESET$1.includes(context.token.toLowerCase());
6873
+ let callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(userOp.callData, context.token, epData.paymasterMetadata.address, UINT256_MAX$1);
6868
6874
  if (requiresAllowanceReset) callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(callDataWithApprove, context.token, epData.paymasterMetadata.address, 0n);
6869
6875
  userOp.callData = callDataWithApprove;
6870
6876
  await this.estimateAndApplyGasLimits(userOp, bundlerRpc, entrypoint, overrides ?? {});
6871
- const approveAmount = await this.calculateUserOperationErc20TokenMaxGasCost(smartAccount, userOp, context.token) * TOKEN_APPROVE_AMOUNT_MULTIPLIER;
6877
+ const approveAmount = await this.calculateUserOperationErc20TokenMaxGasCost(smartAccount, userOp, context.token) * TOKEN_APPROVE_AMOUNT_MULTIPLIER$1;
6872
6878
  callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(oldCallData, context.token, epData.paymasterMetadata.address, approveAmount);
6873
6879
  if (requiresAllowanceReset) callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(callDataWithApprove, context.token, epData.paymasterMetadata.address, 0n);
6874
6880
  userOp.callData = callDataWithApprove;
@@ -6948,6 +6954,396 @@ var CandidePaymaster = class CandidePaymaster extends Paymaster {
6948
6954
  }
6949
6955
  };
6950
6956
  //#endregion
6957
+ //#region src/paymaster/Erc7677Paymaster.ts
6958
+ /** Max value for uint256 */
6959
+ const UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935n;
6960
+ /** Multiplier for token approve amount to cover paymasterAndData cost variance */
6961
+ const TOKEN_APPROVE_AMOUNT_MULTIPLIER = 2n;
6962
+ /**
6963
+ * ERC-20 tokens that require resetting their allowance to 0 before setting a
6964
+ * new approval amount (e.g. USDT on mainnet).
6965
+ */
6966
+ const TOKENS_REQUIRING_ALLOWANCE_RESET = ["0xdac17f958d2ee523a2206206994597c13d831ec7"];
6967
+ /**
6968
+ * Time-to-live for cached Candide `pm_supportedERC20Tokens` responses, applied
6969
+ * only when the fetch is initiated for an exchange-rate lookup. Stub-data
6970
+ * lookups (paymaster address + dummyPaymasterAndData) reuse the cache
6971
+ * indefinitely since those fields are effectively static per EP.
6972
+ */
6973
+ const CANDIDE_TOKEN_QUOTE_TTL_MS = 45e3;
6974
+ var Erc7677Paymaster = class Erc7677Paymaster extends Paymaster {
6975
+ /** The paymaster JSON-RPC endpoint URL */
6976
+ rpcUrl;
6977
+ /** Cached chain ID (hex string). Passed via constructor or resolved from the bundler at first use. */
6978
+ chainId;
6979
+ /** Detected or explicitly set paymaster provider. `null` means no provider-specific features. */
6980
+ provider;
6981
+ /**
6982
+ * Cached Candide `pm_supportedERC20Tokens` response, keyed by lowercase
6983
+ * entrypoint. Used for both token quotes and stub data to avoid a second
6984
+ * round-trip (`pm_getPaymasterStubData`) for Candide-hosted paymasters.
6985
+ *
6986
+ * The cache is indefinite for stub-data lookups but has a TTL for
6987
+ * exchange-rate lookups — see {@link CANDIDE_TOKEN_QUOTE_TTL_MS}.
6988
+ */
6989
+ candideCache = /* @__PURE__ */ new Map();
6990
+ /**
6991
+ * Detect the paymaster provider from the RPC URL hostname.
6992
+ * Returns `null` for unknown hosts or malformed URLs.
6993
+ *
6994
+ * Hostname-based (not substring) so that proxies or paths containing a
6995
+ * provider name (e.g. `https://my-proxy.com/pimlico-compat/...`) are not
6996
+ * misidentified.
6997
+ */
6998
+ static detectProvider(rpcUrl) {
6999
+ let host;
7000
+ try {
7001
+ host = new URL(rpcUrl).hostname.toLowerCase();
7002
+ } catch {
7003
+ return null;
7004
+ }
7005
+ if (host === "pimlico.io" || host.endsWith(".pimlico.io")) return "pimlico";
7006
+ if (host === "candide.dev" || host.endsWith(".candide.dev")) return "candide";
7007
+ return null;
7008
+ }
7009
+ /**
7010
+ * @param rpcUrl - Paymaster JSON-RPC endpoint. Can be the same URL as the
7011
+ * bundler when the provider bundles both (Candide, Pimlico, Alchemy);
7012
+ * can also be a separate paymaster-only endpoint.
7013
+ * @param options
7014
+ * @param options.chainId - Optional chain id as a bigint (e.g. `1n` for
7015
+ * mainnet). When provided, avoids a lookup at first use. Otherwise,
7016
+ * resolved from the bundler via `eth_chainId` on the first call.
7017
+ * @param options.provider - Paymaster provider. `"auto"` (default) detects
7018
+ * from the RPC URL. Set explicitly to override, or `null` to disable.
7019
+ */
7020
+ constructor(rpcUrl, options = {}) {
7021
+ super();
7022
+ this.rpcUrl = rpcUrl;
7023
+ this.chainId = options.chainId != null ? "0x" + options.chainId.toString(16) : null;
7024
+ if (options.provider === void 0 || options.provider === "auto") this.provider = Erc7677Paymaster.detectProvider(rpcUrl);
7025
+ else this.provider = options.provider;
7026
+ }
7027
+ /**
7028
+ * Resolve the chain id, querying the bundler if not provided at construction.
7029
+ */
7030
+ async getChainId(bundlerRpc) {
7031
+ if (this.chainId != null) return this.chainId;
7032
+ const id = await new Bundler(bundlerRpc).chainId();
7033
+ this.chainId = id;
7034
+ return id;
7035
+ }
7036
+ /**
7037
+ * Determine the EntryPoint address from the UserOperation shape.
7038
+ * V6 ops have `initCode`, V8+ ops have `eip7702Auth`, V7 is the default.
7039
+ */
7040
+ resolveEntrypoint(smartAccount, userOperation) {
7041
+ if (smartAccount.entrypointAddress != null && smartAccount.entrypointAddress.trim() !== "") return smartAccount.entrypointAddress;
7042
+ if ("initCode" in userOperation) return ENTRYPOINT_V6;
7043
+ if ("eip7702Auth" in userOperation) return ENTRYPOINT_V8;
7044
+ return ENTRYPOINT_V7;
7045
+ }
7046
+ /**
7047
+ * Low-level ERC-7677 `pm_getPaymasterStubData` call.
7048
+ * Returns dummy paymaster fields intended for gas estimation.
7049
+ *
7050
+ * Most consumers should prefer {@link createPaymasterUserOperation}, which
7051
+ * runs the full stub → estimate → final pipeline. Use this directly if you
7052
+ * need to drive the flow manually.
7053
+ */
7054
+ async getPaymasterStubData(userOperation, entrypoint, chainIdHex, context = {}) {
7055
+ try {
7056
+ return await sendJsonRpcRequest(this.rpcUrl, "pm_getPaymasterStubData", [
7057
+ userOperation,
7058
+ entrypoint,
7059
+ chainIdHex,
7060
+ context
7061
+ ]);
7062
+ } catch (err) {
7063
+ throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterStubData failed", { cause: ensureError(err) });
7064
+ }
7065
+ }
7066
+ /**
7067
+ * Low-level ERC-7677 `pm_getPaymasterData` call.
7068
+ * Returns the final signed paymaster fields.
7069
+ */
7070
+ async getPaymasterData(userOperation, entrypoint, chainIdHex, context = {}) {
7071
+ try {
7072
+ return await sendJsonRpcRequest(this.rpcUrl, "pm_getPaymasterData", [
7073
+ userOperation,
7074
+ entrypoint,
7075
+ chainIdHex,
7076
+ context
7077
+ ]);
7078
+ } catch (err) {
7079
+ throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterData failed", { cause: ensureError(err) });
7080
+ }
7081
+ }
7082
+ /**
7083
+ * Send an arbitrary JSON-RPC request through the paymaster endpoint.
7084
+ * Useful for provider-specific methods that fall outside the ERC-7677 spec.
7085
+ *
7086
+ * @param method - The JSON-RPC method name
7087
+ * @param params - The JSON-RPC params array
7088
+ * @returns The `result` field from the JSON-RPC response
7089
+ */
7090
+ async sendRPCRequest(method, params = []) {
7091
+ try {
7092
+ return await sendJsonRpcRequest(this.rpcUrl, method, params);
7093
+ } catch (err) {
7094
+ throw new AbstractionKitError("PAYMASTER_ERROR", `sendRPCRequest(${method}) failed`, { cause: ensureError(err) });
7095
+ }
7096
+ }
7097
+ /**
7098
+ * Runs the full ERC-7677 pipeline and returns a UserOperation with paymaster
7099
+ * fields populated. The caller is responsible for signing and sending.
7100
+ *
7101
+ * @param smartAccount - Provides the target EntryPoint; not mutated.
7102
+ * @param userOperation - Starting UserOperation. Not mutated — a shallow copy is returned.
7103
+ * @param bundlerRpc - Bundler URL used for gas estimation and, if
7104
+ * `options.chainId` was not provided to the constructor, chain-id lookup.
7105
+ * @param context - Provider-specific paymaster context
7106
+ * (e.g. `{ sponsorshipPolicyId }` or `{ token }`).
7107
+ * @param overrides - Gas estimation overrides and state-override set.
7108
+ *
7109
+ * @returns The UserOperation with paymaster + gas fields populated.
7110
+ */
7111
+ async createPaymasterUserOperation(smartAccount, userOperation, bundlerRpc, context = {}, overrides = {}) {
7112
+ try {
7113
+ const userOp = { ...userOperation };
7114
+ const entrypoint = overrides.entrypoint ?? this.resolveEntrypoint(smartAccount, userOp);
7115
+ const chainIdHex = await this.getChainId(bundlerRpc);
7116
+ if (context.token != null && typeof context.token === "string") return this.tokenPaymasterFlow(smartAccount, userOp, context.token, bundlerRpc, entrypoint, chainIdHex, context, overrides);
7117
+ return this.sponsoredFlow(userOp, bundlerRpc, entrypoint, chainIdHex, context, overrides);
7118
+ } catch (err) {
7119
+ const error = ensureError(err);
7120
+ if (error instanceof AbstractionKitError) throw error;
7121
+ throw new AbstractionKitError("PAYMASTER_ERROR", "createPaymasterUserOperation failed", { cause: error });
7122
+ }
7123
+ }
7124
+ /**
7125
+ * Merge paymaster fields into a UserOperation. Handles both v0.6
7126
+ * (`paymasterAndData`) and v0.7+ split fields.
7127
+ */
7128
+ applyPaymasterFields(userOp, fields) {
7129
+ if ("initCode" in userOp) {
7130
+ if (fields.paymasterAndData != null) userOp.paymasterAndData = fields.paymasterAndData;
7131
+ return;
7132
+ }
7133
+ if (fields.paymaster != null) userOp.paymaster = fields.paymaster;
7134
+ if (fields.paymasterData != null) userOp.paymasterData = fields.paymasterData;
7135
+ if (fields.paymasterVerificationGasLimit != null) userOp.paymasterVerificationGasLimit = BigInt(fields.paymasterVerificationGasLimit);
7136
+ if (fields.paymasterPostOpGasLimit != null) userOp.paymasterPostOpGasLimit = BigInt(fields.paymasterPostOpGasLimit);
7137
+ }
7138
+ /**
7139
+ * Estimate gas limits via the bundler and apply them (with multipliers).
7140
+ * Reads paymaster gas fields back from the bundler when present — some
7141
+ * providers' `pm_getPaymasterStubData` returns `paymasterPostOpGasLimit: 0x1`
7142
+ * as a placeholder, relying on the bundler's estimate for the real value.
7143
+ *
7144
+ * Mirrors CandidePaymaster.estimateAndApplyGasLimits default multipliers
7145
+ * (5%/10%/10% on preVerification/verification/call) for consistent UX.
7146
+ */
7147
+ async estimateAndApplyGasLimits(userOp, bundlerRpc, entrypoint, overrides) {
7148
+ let preVerificationGas = userOp.preVerificationGas;
7149
+ let verificationGasLimit = userOp.verificationGasLimit;
7150
+ let callGasLimit = userOp.callGasLimit;
7151
+ if (overrides.preVerificationGas == null || overrides.verificationGasLimit == null || overrides.callGasLimit == null) {
7152
+ if (bundlerRpc == null) throw new AbstractionKitError("BAD_DATA", "bundlerRpc can't be null if preVerificationGas, verificationGasLimit and callGasLimit are not overridden");
7153
+ const bundler = new Bundler(bundlerRpc);
7154
+ userOp.callGasLimit = 0n;
7155
+ userOp.verificationGasLimit = 0n;
7156
+ userOp.preVerificationGas = 0n;
7157
+ const skipFeeZeroing = this.provider === "pimlico";
7158
+ const inputMaxFeePerGas = userOp.maxFeePerGas;
7159
+ const inputMaxPriorityFeePerGas = userOp.maxPriorityFeePerGas;
7160
+ if (!skipFeeZeroing) {
7161
+ userOp.maxFeePerGas = 0n;
7162
+ userOp.maxPriorityFeePerGas = 0n;
7163
+ }
7164
+ const estimation = await bundler.estimateUserOperationGas(userOp, entrypoint, overrides.state_override_set);
7165
+ if (!skipFeeZeroing) {
7166
+ userOp.maxFeePerGas = inputMaxFeePerGas;
7167
+ userOp.maxPriorityFeePerGas = inputMaxPriorityFeePerGas;
7168
+ }
7169
+ if (estimation.preVerificationGas > preVerificationGas) preVerificationGas = estimation.preVerificationGas;
7170
+ if (estimation.verificationGasLimit > verificationGasLimit) verificationGasLimit = estimation.verificationGasLimit;
7171
+ if (estimation.callGasLimit > callGasLimit) callGasLimit = estimation.callGasLimit;
7172
+ if ("paymaster" in userOp && estimation.paymasterVerificationGasLimit != null) userOp.paymasterVerificationGasLimit = estimation.paymasterVerificationGasLimit;
7173
+ if ("paymaster" in userOp && estimation.paymasterPostOpGasLimit != null) userOp.paymasterPostOpGasLimit = estimation.paymasterPostOpGasLimit;
7174
+ }
7175
+ if (typeof overrides.preVerificationGas === "bigint" && overrides.preVerificationGas < 0n) throw new RangeError("preVerificationGas override can't be negative");
7176
+ if (typeof overrides.verificationGasLimit === "bigint" && overrides.verificationGasLimit < 0n) throw new RangeError("verificationGasLimit override can't be negative");
7177
+ if (typeof overrides.callGasLimit === "bigint" && overrides.callGasLimit < 0n) throw new RangeError("callGasLimit override can't be negative");
7178
+ const applyMultiplier = (value, multiplier) => value + value * BigInt(Math.round((multiplier ?? 0) * 100)) / 10000n;
7179
+ userOp.preVerificationGas = overrides.preVerificationGas ?? applyMultiplier(preVerificationGas, overrides.preVerificationGasPercentageMultiplier ?? 5);
7180
+ userOp.verificationGasLimit = overrides.verificationGasLimit ?? applyMultiplier(verificationGasLimit, overrides.verificationGasLimitPercentageMultiplier ?? 10);
7181
+ userOp.callGasLimit = overrides.callGasLimit ?? applyMultiplier(callGasLimit, overrides.callGasLimitPercentageMultiplier ?? 10);
7182
+ if (entrypoint.toLowerCase() === "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".toLowerCase()) userOp.verificationGasLimit += 40000n;
7183
+ }
7184
+ /**
7185
+ * Fetch token exchange rate and paymaster address via Pimlico's
7186
+ * `pimlico_getTokenQuotes` RPC.
7187
+ *
7188
+ * @returns `exchangeRate` as a bigint scaled by 10^18 (the value of 1 ETH
7189
+ * expressed in the token's smallest unit). Used to compute the token
7190
+ * approval amount via `(exchangeRate * gasCostWei) / 10^18`.
7191
+ */
7192
+ async fetchPimlicoTokenQuote(tokenAddress, entrypoint, chainIdHex) {
7193
+ const quotes = (await sendJsonRpcRequest(this.rpcUrl, "pimlico_getTokenQuotes", [
7194
+ { tokens: [tokenAddress] },
7195
+ entrypoint,
7196
+ chainIdHex
7197
+ ]))?.quotes;
7198
+ if (!Array.isArray(quotes) || quotes.length === 0) throw new AbstractionKitError("PAYMASTER_ERROR", `pimlico_getTokenQuotes returned no quotes for token ${tokenAddress}`);
7199
+ const quote = quotes.find((q) => q.token.toLowerCase() === tokenAddress.toLowerCase());
7200
+ if (quote == null) throw new AbstractionKitError("PAYMASTER_ERROR", `pimlico_getTokenQuotes did not include token ${tokenAddress}`);
7201
+ return {
7202
+ exchangeRate: BigInt(quote.exchangeRate),
7203
+ paymasterAddress: quote.paymaster
7204
+ };
7205
+ }
7206
+ /**
7207
+ * Fetch (and cache) Candide's `pm_supportedERC20Tokens` response for the
7208
+ * given entrypoint. The response carries both exchange rates and the
7209
+ * `dummyPaymasterAndData` used for gas estimation, so one round-trip
7210
+ * suffices for the entire paymaster flow.
7211
+ *
7212
+ * @param options.enforceTTL - When true, re-fetches if the cached entry is
7213
+ * older than {@link CANDIDE_TOKEN_QUOTE_TTL_MS}. Set by exchange-rate
7214
+ * lookups (where staleness matters). Stub-data lookups leave this false
7215
+ * and reuse the cache indefinitely — the paymaster address and
7216
+ * `dummyPaymasterAndData` are effectively static per paymaster version.
7217
+ */
7218
+ async fetchCandideSupportedTokens(entrypoint, options = {}) {
7219
+ const key = entrypoint.toLowerCase();
7220
+ const cached = this.candideCache.get(key);
7221
+ const isStale = cached != null && options.enforceTTL === true && Date.now() - cached.fetchedAt > CANDIDE_TOKEN_QUOTE_TTL_MS;
7222
+ if (cached != null && !isStale) return cached.data;
7223
+ const result = await sendJsonRpcRequest(this.rpcUrl, "pm_supportedERC20Tokens", [entrypoint]);
7224
+ this.candideCache.set(key, {
7225
+ data: result,
7226
+ fetchedAt: Date.now()
7227
+ });
7228
+ return result;
7229
+ }
7230
+ /**
7231
+ * Fetch token exchange rate and paymaster address via Candide's
7232
+ * `pm_supportedERC20Tokens` RPC.
7233
+ *
7234
+ * @returns `exchangeRate` as a bigint scaled by 10^18 (the value of 1 ETH
7235
+ * expressed in the token's smallest unit). Used to compute the token
7236
+ * approval amount via `(exchangeRate * gasCostWei) / 10^18`.
7237
+ */
7238
+ async fetchCandideTokenQuote(tokenAddress, entrypoint) {
7239
+ const result = await this.fetchCandideSupportedTokens(entrypoint, { enforceTTL: true });
7240
+ const token = result.tokens?.find((t) => t.address.toLowerCase() === tokenAddress.toLowerCase());
7241
+ if (token == null) throw new AbstractionKitError("PAYMASTER_ERROR", `${tokenAddress} token is not supported by the Candide paymaster`);
7242
+ return {
7243
+ exchangeRate: BigInt(token.exchangeRate),
7244
+ paymasterAddress: result.paymasterMetadata.address
7245
+ };
7246
+ }
7247
+ /**
7248
+ * Convert Candide's `dummyPaymasterAndData` metadata into a stub result
7249
+ * compatible with {@link applyPaymasterFields}. Handles both v0.6
7250
+ * (concatenated hex string) and v0.7+ (structured) shapes.
7251
+ */
7252
+ candideStubFromMetadata(metadata) {
7253
+ const dummy = metadata.dummyPaymasterAndData;
7254
+ if (typeof dummy === "string") return { paymasterAndData: dummy };
7255
+ return {
7256
+ paymaster: dummy.paymaster,
7257
+ paymasterData: dummy.paymasterData,
7258
+ paymasterVerificationGasLimit: dummy.paymasterVerificationGasLimit,
7259
+ paymasterPostOpGasLimit: dummy.paymasterPostOpGasLimit
7260
+ };
7261
+ }
7262
+ /**
7263
+ * Get stub paymaster data. For Candide-hosted paymasters this derives the
7264
+ * stub from the cached `pm_supportedERC20Tokens` response (no extra
7265
+ * round-trip). For other providers, falls back to `pm_getPaymasterStubData`.
7266
+ */
7267
+ async getStubData(userOperation, entrypoint, chainIdHex, context) {
7268
+ if (this.provider === "candide") {
7269
+ const response = await this.fetchCandideSupportedTokens(entrypoint);
7270
+ return this.candideStubFromMetadata(response.paymasterMetadata);
7271
+ }
7272
+ return this.getPaymasterStubData(userOperation, entrypoint, chainIdHex, context);
7273
+ }
7274
+ /**
7275
+ * Route to the correct provider-specific token quote fetcher.
7276
+ * Returns `null` when no provider is configured.
7277
+ */
7278
+ async fetchProviderTokenQuote(tokenAddress, entrypoint, chainIdHex) {
7279
+ switch (this.provider) {
7280
+ case "pimlico": return this.fetchPimlicoTokenQuote(tokenAddress, entrypoint, chainIdHex);
7281
+ case "candide": return this.fetchCandideTokenQuote(tokenAddress, entrypoint);
7282
+ default: return null;
7283
+ }
7284
+ }
7285
+ /**
7286
+ * Internal token paymaster pipeline. Called from `createPaymasterUserOperation`
7287
+ * when `context.token` is set and the smart account supports approval prepending.
7288
+ *
7289
+ * Three cases:
7290
+ * - **Provider detected**: exchange rate + paymaster address from provider RPC.
7291
+ * - **No provider, `context.exchangeRate` set**: uses provided rate, paymaster
7292
+ * address from stub.
7293
+ * - **No provider, no rate**: falls through to the regular sponsored flow
7294
+ * (developer already handled approval).
7295
+ */
7296
+ async tokenPaymasterFlow(smartAccount, userOp, tokenAddress, bundlerRpc, entrypoint, chainIdHex, context, overrides) {
7297
+ let exchangeRate;
7298
+ let paymasterAddress = null;
7299
+ const providerQuote = await this.fetchProviderTokenQuote(tokenAddress, entrypoint, chainIdHex);
7300
+ if (providerQuote != null) {
7301
+ exchangeRate = providerQuote.exchangeRate;
7302
+ paymasterAddress = providerQuote.paymasterAddress;
7303
+ } else if (context.exchangeRate != null) {
7304
+ try {
7305
+ exchangeRate = BigInt(context.exchangeRate);
7306
+ } catch (err) {
7307
+ throw new AbstractionKitError("PAYMASTER_ERROR", `context.exchangeRate could not be parsed as a bigint: ${String(context.exchangeRate)}`, { cause: ensureError(err) });
7308
+ }
7309
+ if (exchangeRate <= 0n) throw new AbstractionKitError("PAYMASTER_ERROR", `context.exchangeRate must be > 0, got ${exchangeRate}`);
7310
+ } else return this.sponsoredFlow(userOp, bundlerRpc, entrypoint, chainIdHex, context, overrides);
7311
+ const stub = await this.getStubData(userOp, entrypoint, chainIdHex, context);
7312
+ this.applyPaymasterFields(userOp, stub);
7313
+ if (paymasterAddress == null) if (context.paymasterAddress != null) paymasterAddress = context.paymasterAddress;
7314
+ else if ("initCode" in userOp && stub.paymasterAndData != null) paymasterAddress = "0x" + stub.paymasterAndData.slice(2, 42);
7315
+ else if (stub.paymaster != null) paymasterAddress = stub.paymaster;
7316
+ else throw new AbstractionKitError("PAYMASTER_ERROR", "pm_getPaymasterStubData did not return a paymaster address. Pass paymasterAddress in the context or set a provider.");
7317
+ const originalCallData = userOp.callData;
7318
+ const requiresAllowanceReset = overrides.resetApproval ?? TOKENS_REQUIRING_ALLOWANCE_RESET.includes(tokenAddress.toLowerCase());
7319
+ let callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(userOp.callData, tokenAddress, paymasterAddress, UINT256_MAX);
7320
+ if (requiresAllowanceReset) callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(callDataWithApprove, tokenAddress, paymasterAddress, 0n);
7321
+ userOp.callData = callDataWithApprove;
7322
+ await this.estimateAndApplyGasLimits(userOp, bundlerRpc, entrypoint, overrides);
7323
+ const maxGasCostWei = calculateUserOperationMaxGasCost(userOp);
7324
+ const approveAmount = exchangeRate * maxGasCostWei / 10n ** 18n * TOKEN_APPROVE_AMOUNT_MULTIPLIER;
7325
+ callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(originalCallData, tokenAddress, paymasterAddress, approveAmount);
7326
+ if (requiresAllowanceReset) callDataWithApprove = smartAccount.prependTokenPaymasterApproveToCallData(callDataWithApprove, tokenAddress, paymasterAddress, 0n);
7327
+ userOp.callData = callDataWithApprove;
7328
+ const final = await this.getPaymasterData(userOp, entrypoint, chainIdHex, context);
7329
+ this.applyPaymasterFields(userOp, final);
7330
+ return userOp;
7331
+ }
7332
+ /**
7333
+ * The regular (non-token) sponsored flow: stub → estimate → final.
7334
+ * Extracted to allow `tokenPaymasterFlow` to fall through to it for Case C.
7335
+ */
7336
+ async sponsoredFlow(userOp, bundlerRpc, entrypoint, chainIdHex, context, overrides) {
7337
+ const stub = await this.getStubData(userOp, entrypoint, chainIdHex, context);
7338
+ this.applyPaymasterFields(userOp, stub);
7339
+ await this.estimateAndApplyGasLimits(userOp, bundlerRpc, entrypoint, overrides);
7340
+ if (stub.isFinal === true) return userOp;
7341
+ const final = await this.getPaymasterData(userOp, entrypoint, chainIdHex, context);
7342
+ this.applyPaymasterFields(userOp, final);
7343
+ return userOp;
7344
+ }
7345
+ };
7346
+ //#endregion
6951
7347
  //#region src/paymaster/AllowAllPaymaster.ts
6952
7348
  /**
6953
7349
  * A paymaster that sponsors all UserOperations unconditionally.
@@ -7093,6 +7489,7 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
7093
7489
  ENTRYPOINT_V9: () => ENTRYPOINT_V9,
7094
7490
  EOADummySignerSignaturePair: () => EOADummySignerSignaturePair,
7095
7491
  EXECUTE_RECOVERY_PRIMARY_TYPE: () => EXECUTE_RECOVERY_PRIMARY_TYPE,
7492
+ Erc7677Paymaster: () => Erc7677Paymaster,
7096
7493
  ExperimentalAllowAllParallelPaymaster: () => ExperimentalAllowAllParallelPaymaster,
7097
7494
  GasOption: () => GasOption,
7098
7495
  Operation: () => Operation,
@@ -7143,6 +7540,6 @@ var abstractionkit_exports = /* @__PURE__ */ __exportAll({
7143
7540
  simulateUserOperationWithTenderlyAndCreateShareLink: () => simulateUserOperationWithTenderlyAndCreateShareLink
7144
7541
  });
7145
7542
  //#endregion
7146
- export { ALLOWANCE_MODULE_V0_1_0_ADDRESS, AbstractionKitError, AllowanceModule, BaseUserOperationDummyValues, Bundler, CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS, CALIBUR_UNISWAP_V1_0_0_SINGLETON_ADDRESS, Calibur7702Account, CaliburKeyType, CandidePaymaster, DEFAULT_SECP256R1_PRECOMPILE_ADDRESS, EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, EIP712_RECOVERY_MODULE_TYPE, EIP712_SAFE_OPERATION_PRIMARY_TYPE, EIP712_SAFE_OPERATION_V6_TYPE, EIP712_SAFE_OPERATION_V7_TYPE, ENTRYPOINT_V6, ENTRYPOINT_V7, ENTRYPOINT_V8, ENTRYPOINT_V9, EOADummySignerSignaturePair, EXECUTE_RECOVERY_PRIMARY_TYPE, ExperimentalAllowAllParallelPaymaster, GasOption, Operation, PolygonChain, SAFE_MESSAGE_MODULE_TYPE, SAFE_MESSAGE_PRIMARY_TYPE, SafeAccountFactory, SafeAccountV0_2_0, SafeAccountV0_3_0, SafeAccountV1_5_0_M_0_3_0, SafeModuleExecutorFunctionSelector, SafeMultiChainSigAccountV1, SendUseroperationResponse, Simple7702Account, Simple7702AccountV09, SmartAccount, SmartAccountFactory, SocialRecoveryModule, SocialRecoveryModuleGracePeriodSelector, WebauthnDummySignerSignaturePair, WorldIdPermissionlessPaymaster, ZeroAddress, abstractionkit_exports as abstractionkit, calculateUserOperationMaxGasCost, callTenderlySimulateBundle, createAndSignEip7702DelegationAuthorization, createAndSignEip7702RawTransaction, createAndSignLegacyRawTransaction, createCallData, createEip7702DelegationAuthorizationHash, createEip7702TransactionHash, createUserOperationHash, createWorldIdSignal, fetchAccountNonce, fetchGasPrice, getBalanceOf, getDelegatedAddress, getDepositInfo, getFunctionSelector, getSafeMessageEip712Data, sendJsonRpcRequest, shareTenderlySimulationAndCreateLink, signHash, simulateSenderCallDataWithTenderly, simulateSenderCallDataWithTenderlyAndCreateShareLink, simulateUserOperationCallDataWithTenderly, simulateUserOperationCallDataWithTenderlyAndCreateShareLink, simulateUserOperationWithTenderly, simulateUserOperationWithTenderlyAndCreateShareLink };
7543
+ export { ALLOWANCE_MODULE_V0_1_0_ADDRESS, AbstractionKitError, AllowanceModule, BaseUserOperationDummyValues, Bundler, CALIBUR_CANDIDE_V0_1_0_SINGLETON_ADDRESS, CALIBUR_UNISWAP_V1_0_0_SINGLETON_ADDRESS, Calibur7702Account, CaliburKeyType, CandidePaymaster, DEFAULT_SECP256R1_PRECOMPILE_ADDRESS, EIP712_MULTI_CHAIN_OPERATIONS_PRIMARY_TYPE, EIP712_MULTI_CHAIN_OPERATIONS_TYPE, EIP712_RECOVERY_MODULE_TYPE, EIP712_SAFE_OPERATION_PRIMARY_TYPE, EIP712_SAFE_OPERATION_V6_TYPE, EIP712_SAFE_OPERATION_V7_TYPE, ENTRYPOINT_V6, ENTRYPOINT_V7, ENTRYPOINT_V8, ENTRYPOINT_V9, EOADummySignerSignaturePair, EXECUTE_RECOVERY_PRIMARY_TYPE, Erc7677Paymaster, ExperimentalAllowAllParallelPaymaster, GasOption, Operation, PolygonChain, SAFE_MESSAGE_MODULE_TYPE, SAFE_MESSAGE_PRIMARY_TYPE, SafeAccountFactory, SafeAccountV0_2_0, SafeAccountV0_3_0, SafeAccountV1_5_0_M_0_3_0, SafeModuleExecutorFunctionSelector, SafeMultiChainSigAccountV1, SendUseroperationResponse, Simple7702Account, Simple7702AccountV09, SmartAccount, SmartAccountFactory, SocialRecoveryModule, SocialRecoveryModuleGracePeriodSelector, WebauthnDummySignerSignaturePair, WorldIdPermissionlessPaymaster, ZeroAddress, abstractionkit_exports as abstractionkit, calculateUserOperationMaxGasCost, callTenderlySimulateBundle, createAndSignEip7702DelegationAuthorization, createAndSignEip7702RawTransaction, createAndSignLegacyRawTransaction, createCallData, createEip7702DelegationAuthorizationHash, createEip7702TransactionHash, createUserOperationHash, createWorldIdSignal, fetchAccountNonce, fetchGasPrice, getBalanceOf, getDelegatedAddress, getDepositInfo, getFunctionSelector, getSafeMessageEip712Data, sendJsonRpcRequest, shareTenderlySimulationAndCreateLink, signHash, simulateSenderCallDataWithTenderly, simulateSenderCallDataWithTenderlyAndCreateShareLink, simulateUserOperationCallDataWithTenderly, simulateUserOperationCallDataWithTenderlyAndCreateShareLink, simulateUserOperationWithTenderly, simulateUserOperationWithTenderlyAndCreateShareLink };
7147
7544
 
7148
7545
  //# sourceMappingURL=index.mjs.map