diff --git a/packages/alchemy/src/middleware/gas-fees.ts b/packages/alchemy/src/middleware/gas-fees.ts index 316bba5a1..92992dbd9 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 { applyUserOpOverrideOrFeeOption } from "@alchemy/aa-core"; import type { AlchemyProvider } from "../provider.js"; import type { ClientWithAlchemyMethods } from "./client.js"; @@ -6,34 +6,29 @@ 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 maxFeePerGas = - overrides?.maxFeePerGas != null - ? overrides?.maxFeePerGas - : await estimateMaxFeePerGas(maxPriorityFeePerGas); + const maxPriorityFeePerGas = applyUserOpOverrideOrFeeOption( + maxPriorityFeePerGasEstimate, + overrides?.maxPriorityFeePerGas, + feeOptions?.maxPriorityFeePerGas + ); + const maxFeePerGas = applyUserOpOverrideOrFeeOption( + baseFeePerGas + BigInt(maxPriorityFeePerGas), + overrides?.maxFeePerGas, + feeOptions?.maxFeePerGas + ); return { ...struct, diff --git a/packages/alchemy/src/middleware/gas-manager.ts b/packages/alchemy/src/middleware/gas-manager.ts index 9ac692e61..b1fc6eabb 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 a43fd7796..c6dada652 100644
--- a/packages/core/src/index.ts
+++ b/packages/core/src/index.ts
@@ -49,7 +49,9 @@ export type * from "./types.js";
export type * from "./utils/index.js";
export {
ChainSchema,
- applyFeeOption,
+ applyUserOpFeeOption,
+ applyUserOpOverride,
+ applyUserOpOverrideOrFeeOption,
asyncPipe,
bigIntMax,
bigIntPercent,
diff --git a/packages/core/src/provider/base.ts b/packages/core/src/provider/base.ts
index 4465be42b..b6117fcfe 100644
--- a/packages/core/src/provider/base.ts
+++ b/packages/core/src/provider/base.ts
@@ -32,7 +32,7 @@ import {
type UserOperationStruct,
} from "../types.js";
import {
- applyFeeOption,
+ applyUserOpOverrideOrFeeOption,
asyncPipe,
bigIntMax,
bigIntPercent,
@@ -521,41 +521,31 @@ export class SmartAccountProvider<
overrides,
feeOptions
) => {
- let { callGasLimit, verificationGasLimit, preVerificationGas } =
- overrides ?? {};
-
- if (
- callGasLimit == null ||
- verificationGasLimit == null ||
- preVerificationGas == null
- ) {
- const request = deepHexlify(await resolveProperties(struct));
- const estimates = await this.rpcClient.estimateUserOperationGas(
- request,
- this.getEntryPointAddress()
- );
+ const request = deepHexlify(await resolveProperties(struct));
+ const estimates = await this.rpcClient.estimateUserOperationGas(
+ request,
+ this.getEntryPointAddress()
+ );
- callGasLimit =
- callGasLimit ??
- applyFeeOption(estimates.callGasLimit, feeOptions?.callGasLimit);
- verificationGasLimit =
- verificationGasLimit ??
- applyFeeOption(
- estimates.verificationGasLimit,
- feeOptions?.verificationGasLimit
- );
- preVerificationGas =
- preVerificationGas ??
- applyFeeOption(
- estimates.preVerificationGas,
- feeOptions?.preVerificationGas
- );
- }
+ const callGasLimit = applyUserOpOverrideOrFeeOption(
+ estimates.callGasLimit,
+ overrides?.callGasLimit,
+ feeOptions?.callGasLimit
+ );
+ const verificationGasLimit = applyUserOpOverrideOrFeeOption(
+ estimates.verificationGasLimit,
+ overrides?.verificationGasLimit,
+ feeOptions?.verificationGasLimit
+ );
+ const preVerificationGas = applyUserOpOverrideOrFeeOption(
+ estimates.preVerificationGas,
+ overrides?.preVerificationGas,
+ feeOptions?.preVerificationGas
+ );
struct.callGasLimit = callGasLimit;
struct.verificationGasLimit = verificationGasLimit;
struct.preVerificationGas = preVerificationGas;
-
return struct;
};
@@ -564,11 +554,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
//
@@ -577,30 +562,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
+
+ const feeData = await this.rpcClient.estimateFeesPerGas();
+ if (!feeData.maxFeePerGas || !feeData.maxPriorityFeePerGas) {
+ throw new Error(
+ "feeData is missing maxFeePerGas or maxPriorityFeePerGas"
);
+ }
- return BigInt(baseFee) + BigInt(maxPriorityFeePerGas);
- };
+ let maxPriorityFeePerGas: BigNumberish =
+ await this.rpcClient.estimateMaxPriorityFeePerGas();
+ maxPriorityFeePerGas = applyUserOpOverrideOrFeeOption(
+ maxPriorityFeePerGas,
+ overrides?.maxPriorityFeePerGas,
+ feeOptions?.maxPriorityFeePerGas
+ );
- struct.maxPriorityFeePerGas =
- overrides?.maxPriorityFeePerGas != null
- ? overrides?.maxPriorityFeePerGas
- : await estimateMaxPriorityFeePerGas();
- struct.maxFeePerGas =
- overrides?.maxFeePerGas != null
- ? overrides?.maxFeePerGas
- : await estimateMaxFeePerGas(struct.maxPriorityFeePerGas);
+ let maxFeePerGas: BigNumberish =
+ feeData.maxFeePerGas -
+ feeData.maxPriorityFeePerGas +
+ BigInt(maxPriorityFeePerGas);
+
+ maxFeePerGas = applyUserOpOverrideOrFeeOption(
+ maxFeePerGas,
+ overrides?.maxFeePerGas,
+ feeOptions?.maxFeePerGas
+ );
+ struct.maxFeePerGas = maxFeePerGas;
+ struct.maxPriorityFeePerGas = maxPriorityFeePerGas;
return struct;
};
diff --git a/packages/core/src/provider/types.ts b/packages/core/src/provider/types.ts
index 1efdde77a..a02f1b608 100644
--- a/packages/core/src/provider/types.ts
+++ b/packages/core/src/provider/types.ts
@@ -330,7 +330,6 @@ export interface ISmartAccountProvider<
* prior to execution.
*
* @param override - a function for overriding the default feeDataGetter middleware
- * @param feeOptions - optional FeeDataFeeOptions to set at the global level of the provider.
* @returns
*/
withFeeDataGetter: (override: FeeDataMiddleware) => this;
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index d3a889aa9..c6af246ad 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -43,17 +43,18 @@ export type UserOperationFeeOptions = z.infer<
typeof UserOperationFeeOptionsSchema
>;
-export type UserOperationOverrides = Partial<
- Pick<
- UserOperationStruct,
- | "callGasLimit"
- | "maxFeePerGas"
- | "maxPriorityFeePerGas"
- | "paymasterAndData"
- | "preVerificationGas"
- | "verificationGasLimit"
- >
->;
+export type UserOperationOverrides = Partial<{
+ callGasLimit: UserOperationStruct["callGasLimit"] | Percentage;
+ maxFeePerGas: UserOperationStruct["maxFeePerGas"] | Percentage;
+ maxPriorityFeePerGas:
+ | UserOperationStruct["maxPriorityFeePerGas"]
+ | Percentage;
+ preVerificationGas: UserOperationStruct["preVerificationGas"] | Percentage;
+ verificationGasLimit:
+ | UserOperationStruct["verificationGasLimit"]
+ | Percentage;
+ paymasterAndData: UserOperationStruct["paymasterAndData"];
+}>;
// represents the request as it needs to be formatted for RPC requests
export interface UserOperationRequest {
diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts
index 89a2ceb1a..75a835e7b 100644
--- a/packages/core/src/utils/index.ts
+++ b/packages/core/src/utils/index.ts
@@ -5,10 +5,8 @@ import type {
BigNumberish,
Percentage,
PromiseOrValue,
- UserOperationFeeOptionsField,
UserOperationRequest,
} from "../types.js";
-import { bigIntClamp, bigIntPercent } from "./bigint.js";
import { BigNumberishSchema, PercentageSchema } from "./schema.js";
/**
@@ -97,24 +95,6 @@ export function deepHexlify(obj: any): any {
);
}
-export function applyFeeOption(
- value: BigNumberish | undefined,
- feeOption?: UserOperationFeeOptionsField
-): BigNumberish {
- if (feeOption == null) {
- return value ?? 0n;
- }
- return value
- ? bigIntClamp(
- feeOption.percentage
- ? bigIntPercent(value, BigInt(100 + feeOption.percentage))
- : value,
- feeOption.min,
- feeOption.max
- )
- : feeOption.min ?? 0n;
-}
-
/**
* Generates a hash for a UserOperation valid from entrypoint version 0.6 onwards
*
@@ -183,11 +163,11 @@ export function defineReadOnly