diff --git a/packages/alchemy/src/middleware/gas-fees.ts b/packages/alchemy/src/middleware/gas-fees.ts index 478ae4d56a..1ecb4994c5 100644 --- a/packages/alchemy/src/middleware/gas-fees.ts +++ b/packages/alchemy/src/middleware/gas-fees.ts @@ -1,4 +1,4 @@ -import { applyFeeOption, type BigNumberish } from "@alchemy/aa-core"; +import { applyFeeOption, applyUserOperationOverride } from "@alchemy/aa-core"; import type { AlchemyProvider } from "../provider.js"; import type { ClientWithAlchemyMethods } from "./client.js"; @@ -6,40 +6,43 @@ export const withAlchemyGasFeeEstimator = ( provider: AlchemyProvider ): AlchemyProvider => { provider.withFeeDataGetter(async (struct, overrides, feeOptions) => { - const maxPriorityFeePerGas = - overrides?.maxPriorityFeePerGas != null - ? overrides?.maxPriorityFeePerGas - : // it's a fair assumption that if someone is using this Alchemy Middleware, then they are using Alchemy RPC - applyFeeOption( - await (provider.rpcClient as ClientWithAlchemyMethods).request({ - method: "rundler_maxPriorityFeePerGas", - params: [], - }), - feeOptions?.maxPriorityFeePerGas - ); + let [block, maxPriorityFeePerGasEstimate] = await Promise.all([ + provider.rpcClient.getBlock({ blockTag: "latest" }), + // it's a fair assumption that if someone is using this Alchemy Middleware, then they are using Alchemy RPC + (provider.rpcClient as ClientWithAlchemyMethods).request({ + method: "rundler_maxPriorityFeePerGas", + params: [], + }), + ]); + const baseFeePerGas = block.baseFeePerGas; + if (baseFeePerGas == null) { + throw new Error("baseFeePerGas is null"); + } - const estimateMaxFeePerGas = async (priorityFeePerGas: BigNumberish) => { - const block = await provider.rpcClient.getBlock({ blockTag: "latest" }); - const baseFeePerGas = block.baseFeePerGas; - if (baseFeePerGas == null) { - throw new Error("baseFeePerGas is null"); - } - return applyFeeOption( - baseFeePerGas + BigInt(priorityFeePerGas), - feeOptions?.maxFeePerGas + const maxPriorityFeePerGas = + applyUserOperationOverride( + maxPriorityFeePerGasEstimate, + overrides?.maxPriorityFeePerGas + ) || + applyFeeOption( + maxPriorityFeePerGasEstimate, + feeOptions?.maxPriorityFeePerGas ); - }; - const maxFeePerGas = - overrides?.maxFeePerGas != null - ? overrides?.maxFeePerGas - : await estimateMaxFeePerGas(maxPriorityFeePerGas); + applyUserOperationOverride( + baseFeePerGas + BigInt(maxPriorityFeePerGas), + overrides?.maxPriorityFeePerGas + ) || + applyFeeOption( + baseFeePerGas + BigInt(maxPriorityFeePerGas), + feeOptions?.maxPriorityFeePerGas + ); return { ...struct, maxPriorityFeePerGas, maxFeePerGas, }; - }, provider.feeOptions); + }); return provider; }; diff --git a/packages/alchemy/src/middleware/gas-manager.ts b/packages/alchemy/src/middleware/gas-manager.ts index e301969c06..d9fcb07feb 100644 --- a/packages/alchemy/src/middleware/gas-manager.ts +++ b/packages/alchemy/src/middleware/gas-manager.ts @@ -1,6 +1,7 @@ import { deepHexlify, filterUndefined, + isBigNumberish, isPercentage, resolveProperties, type AccountMiddlewareFn, @@ -169,15 +170,25 @@ const withAlchemyGasAndPaymasterAndDataMiddleware =
(
const overrideField = (
field: keyof UserOperationFeeOptions
): Hex | Percentage | undefined => {
- // one-off absolute override
if (overrides?.[field] != null) {
- return deepHexlify(overrides[field]);
+ // one-off absolute override
+ if (isBigNumberish(overrides[field])) {
+ return deepHexlify(overrides[field]);
+ }
+ // one-off percentage overrides
+ else {
+ return {
+ percentage:
+ 100 + Number((overrides[field] as Percentage).percentage),
+ };
+ }
}
// provider level fee options with percentage
if (isPercentage(feeOptions?.[field])) {
return {
- percentage: 100 + Number(feeOptions?.[field]?.percentage),
+ percentage:
+ 100 + Number((feeOptions![field] as Percentage).percentage),
};
}
diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts
index a43fd77964..c1453bc03a 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -50,6 +50,7 @@ export type * from "./utils/index.js";
export {
ChainSchema,
applyFeeOption,
+ applyUserOperationOverride,
asyncPipe,
bigIntMax,
bigIntPercent,
diff --git a/packages/core/src/provider/base.ts b/packages/core/src/provider/base.ts
index 3717052260..791f1976bc 100644
--- a/packages/core/src/provider/base.ts
+++ b/packages/core/src/provider/base.ts
@@ -33,6 +33,7 @@ import {
} from "../types.js";
import {
applyFeeOption,
+ applyUserOperationOverride,
asyncPipe,
bigIntMax,
bigIntPercent,
@@ -50,10 +51,7 @@ import { createSmartAccountProviderConfigSchema } from "./schema.js";
import type {
AccountMiddlewareFn,
AccountMiddlewareOverrideFn,
- FeeDataFeeOptions,
FeeDataMiddleware,
- FeeOptionsMiddleware,
- GasEstimatorFeeOptions,
GasEstimatorMiddleware,
ISmartAccountProvider,
PaymasterAndDataMiddleware,
@@ -407,20 +405,15 @@ export class SmartAccountProvider<
const { maxFeePerGas, maxPriorityFeePerGas } =
await this._runMiddlewareStack(uoToSubmit, overrides);
- const _maxPriorityFeePerGas =
- bigIntMax(
- BigInt(maxPriorityFeePerGas ?? 0n),
- bigIntPercent(uoToDrop.maxPriorityFeePerGas, 110n)
- ) ?? 0n;
const _overrides: UserOperationOverrides = {
maxFeePerGas: bigIntMax(
BigInt(maxFeePerGas ?? 0n),
- bigIntPercent(
- BigInt(uoToDrop.maxFeePerGas) - _maxPriorityFeePerGas,
- 110n
- ) + _maxPriorityFeePerGas
+ bigIntPercent(uoToDrop.maxFeePerGas, 110n)
+ ),
+ maxPriorityFeePerGas: bigIntMax(
+ BigInt(maxPriorityFeePerGas ?? 0n),
+ bigIntPercent(uoToDrop.maxPriorityFeePerGas, 110n)
),
- maxPriorityFeePerGas: _maxPriorityFeePerGas,
paymasterAndData: uoToDrop.paymasterAndData,
};
@@ -530,41 +523,39 @@ export class SmartAccountProvider<
overrides,
feeOptions
) => {
- let { callGasLimit, verificationGasLimit, preVerificationGas } =
- overrides ?? {};
+ const request = deepHexlify(await resolveProperties(struct));
+ const estimates = await this.rpcClient.estimateUserOperationGas(
+ request,
+ this.getEntryPointAddress()
+ );
- if (
- callGasLimit == null ||
- verificationGasLimit == null ||
- preVerificationGas == null
- ) {
- const request = deepHexlify(await resolveProperties(struct));
- const estimates = await this.rpcClient.estimateUserOperationGas(
- request,
- this.getEntryPointAddress()
+ const callGasLimit =
+ applyUserOperationOverride(
+ estimates.callGasLimit,
+ overrides?.callGasLimit
+ ) || applyFeeOption(estimates.callGasLimit, feeOptions?.callGasLimit);
+ const verificationGasLimit =
+ applyUserOperationOverride(
+ estimates.verificationGasLimit,
+ overrides?.verificationGasLimit
+ ) ||
+ applyFeeOption(
+ estimates.verificationGasLimit,
+ feeOptions?.verificationGasLimit
+ );
+ const preVerificationGas =
+ applyUserOperationOverride(
+ estimates.preVerificationGas,
+ overrides?.preVerificationGas
+ ) ||
+ applyFeeOption(
+ estimates.preVerificationGas,
+ feeOptions?.preVerificationGas
);
-
- callGasLimit =
- callGasLimit ??
- applyFeeOption(estimates.callGasLimit, feeOptions?.callGasLimit);
- verificationGasLimit =
- verificationGasLimit ??
- applyFeeOption(
- estimates.verificationGasLimit,
- feeOptions?.verificationGasLimit
- );
- preVerificationGas =
- preVerificationGas ??
- applyFeeOption(
- estimates.preVerificationGas,
- feeOptions?.preVerificationGas
- );
- }
struct.callGasLimit = callGasLimit;
struct.verificationGasLimit = verificationGasLimit;
struct.preVerificationGas = preVerificationGas;
-
return struct;
};
@@ -573,11 +564,6 @@ export class SmartAccountProvider<
overrides,
feeOptions
) => {
- const estimateMaxPriorityFeePerGas = async () => {
- const estimate = await this.rpcClient.estimateMaxPriorityFeePerGas();
- return applyFeeOption(estimate, feeOptions?.maxPriorityFeePerGas);
- };
-
// maxFeePerGas must be at least the sum of maxPriorityFeePerGas and baseFee
// so we need to accommodate for the fee option applied maxPriorityFeePerGas for the maxFeePerGas
//
@@ -586,59 +572,35 @@ export class SmartAccountProvider<
//
// Refer to https://docs.alchemy.com/docs/maxpriorityfeepergas-vs-maxfeepergas
// for more information about maxFeePerGas and maxPriorityFeePerGas
- const estimateMaxFeePerGas = async (maxPriorityFeePerGas: BigNumberish) => {
- const feeData = await this.rpcClient.estimateFeesPerGas();
- if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas) {
- throw new Error(
- "feeData is missing maxFeePerGas or maxPriorityFeePerGas"
- );
- }
- const baseFee = applyFeeOption(
- feeData.maxFeePerGas - feeData.maxPriorityFeePerGas,
- feeOptions?.maxFeePerGas
- );
-
- return BigInt(baseFee) + BigInt(maxPriorityFeePerGas);
- };
- struct.maxPriorityFeePerGas =
- overrides?.maxPriorityFeePerGas != null
- ? overrides?.maxPriorityFeePerGas
- : await estimateMaxPriorityFeePerGas();
- struct.maxFeePerGas =
- overrides?.maxFeePerGas != null
- ? overrides?.maxFeePerGas
- : await estimateMaxFeePerGas(struct.maxPriorityFeePerGas);
-
- return struct;
- };
-
- readonly feeOptionsMiddleware: AccountMiddlewareFn = async (
- struct,
- overrides
- ) => {
- const resolved = await resolveProperties