-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(alchemy): use rundler_getLocalRequiredFees for fee estimate #71
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export const API_KEY = process.env.API_KEY!; | ||
export const RPC_URL = process.env.RPC_URL; | ||
export const API_KEY = process.env.API_KEY; | ||
export const OWNER_MNEMONIC = process.env.OWNER_MNEMONIC!; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export const API_KEY = process.env.API_KEY!; | ||
export const RPC_URL = process.env.RPC_URL; | ||
export const API_KEY = process.env.API_KEY; | ||
export const OWNER_MNEMONIC = process.env.OWNER_MNEMONIC!; | ||
export const PAYMASTER_POLICY_ID = process.env.PAYMASTER_POLICY_ID!; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { | ||
type PublicErc4337Client, | ||
type UserOperationRequest, | ||
} from "@alchemy/aa-core"; | ||
import type { Address, Hex } from "viem"; | ||
|
||
export type ClientWithAlchemyMethods = PublicErc4337Client & { | ||
request: PublicErc4337Client["request"] & | ||
{ | ||
request(args: { | ||
method: "alchemy_requestPaymasterAndData"; | ||
params: [ | ||
{ | ||
policyId: string; | ||
entryPoint: Address; | ||
userOperation: UserOperationRequest; | ||
} | ||
]; | ||
}): Promise<{ paymasterAndData: Hex }>; | ||
|
||
request(args: { | ||
method: "alchemy_requestGasAndPaymasterAndData"; | ||
params: [ | ||
{ | ||
policyId: string; | ||
entryPoint: Address; | ||
userOperation: UserOperationRequest; | ||
dummySignature: Hex; | ||
} | ||
]; | ||
}): Promise<{ | ||
paymasterAndData: Hex; | ||
callGasLimit: Hex; | ||
verificationGasLimit: Hex; | ||
preVerificationGas: Hex; | ||
maxFeePerGas: Hex; | ||
maxPriorityFeePerGas: Hex; | ||
}>; | ||
|
||
request(args: { | ||
method: "rundler_getLocalRequiredFees"; | ||
params: []; | ||
}): Promise<{ | ||
blockNumber: Hex; | ||
baseFeePerGas: Hex; | ||
minimum: { | ||
maxFeePerGas: Hex; | ||
maxPriorityFeePerGas: Hex; | ||
}; | ||
recommended: { | ||
maxFeePerGas: Hex; | ||
maxPriorityFeePerGas: Hex; | ||
}; | ||
}>; | ||
}["request"]; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,17 @@ | ||
import type { AlchemyProvider } from "../provider.js"; | ||
|
||
export enum GasFeeStrategy { | ||
DEFAULT = "DEFAULT", | ||
PERCENT_OF_BASE_FEE = "PERCENT_OF_BASE_FEE", | ||
PRIORITY_FEE_INCREASE_PERCENT = "PRIORITY_FEE_INCREASE_PERCENT", | ||
} | ||
|
||
export interface GasFeeMode { | ||
strategy: GasFeeStrategy; | ||
value: bigint; | ||
} | ||
import type { ClientWithAlchemyMethods } from "./client.js"; | ||
|
||
export const withAlchemyGasFeeEstimator = ( | ||
provider: AlchemyProvider, | ||
feeMode: GasFeeMode, | ||
maxPriorityFeeBufferPercent: bigint | ||
provider: AlchemyProvider | ||
): AlchemyProvider => { | ||
if (feeMode.strategy === GasFeeStrategy.DEFAULT) { | ||
return provider; | ||
} | ||
|
||
provider.withFeeDataGetter(async () => { | ||
const block = await provider.rpcClient.getBlock({ blockTag: "latest" }); | ||
const baseFeePerGas = block.baseFeePerGas; | ||
if (baseFeePerGas == null) { | ||
throw new Error("baseFeePerGas is null"); | ||
} | ||
// add a buffer here to account for potential spikes in priority fee | ||
const maxPriorityFeePerGas = | ||
(BigInt(await provider.rpcClient.getMaxPriorityFeePerGas()) * | ||
(100n + maxPriorityFeeBufferPercent)) / | ||
100n; | ||
// add 25% overhead to ensure mine | ||
const baseFeeScaled = (baseFeePerGas * 5n) / 4n; | ||
|
||
const prioFee = ((): bigint => { | ||
switch (feeMode.strategy) { | ||
case GasFeeStrategy.PERCENT_OF_BASE_FEE: | ||
return (baseFeeScaled * feeMode.value) / 100n; | ||
case GasFeeStrategy.PRIORITY_FEE_INCREASE_PERCENT: | ||
// add 10% to required priority fee to ensure mine | ||
return (maxPriorityFeePerGas * (100n + feeMode.value)) / 100n; | ||
default: | ||
throw new Error("fee mode not supported"); | ||
} | ||
})(); | ||
|
||
return { | ||
maxPriorityFeePerGas: prioFee, | ||
maxFeePerGas: baseFeeScaled + prioFee, | ||
}; | ||
const result = await ( | ||
provider.rpcClient as ClientWithAlchemyMethods | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should update the typing on the |
||
).request({ | ||
method: "rundler_getLocalRequiredFees", | ||
params: [], | ||
}); | ||
return result.recommended; | ||
}); | ||
return provider; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
export const API_KEY = process.env.API_KEY!; | ||
export const RPC_URL = process.env.RPC_URL; | ||
export const API_KEY = process.env.API_KEY; | ||
export const OWNER_MNEMONIC = process.env.OWNER_MNEMONIC!; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -293,8 +293,8 @@ export class SmartAccountProvider< | |
const initCode = await this.account.getInitCode(); | ||
const uoStruct = await asyncPipe( | ||
this.dummyPaymasterDataMiddleware, | ||
this.gasEstimator, | ||
this.feeDataGetter, | ||
this.gasEstimator, | ||
Comment on lines
-296
to
+297
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @moldy530 can you check my logic on this change. We want the fees to get estimated and applied before gas is estimated as gas estimation can sometimes rely on fees (e.g. as it will on Optimism). Does this have that intended impact? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yup! pipe executes sequentially so this will do fees then gas |
||
this.paymasterDataMiddleware, | ||
this.customMiddleware ?? noOpMiddleware, | ||
// This applies the overrides if they've been passed in | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the use case for this?
also nit:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be able to run the e2e tests against something other than Alchemy prod
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can put this in a separate PR, just needed it for my current testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all good, we can keep it here