diff --git a/examples/alchemy-daapp/src/surfaces/onboarding/OnboardingController.ts b/examples/alchemy-daapp/src/surfaces/onboarding/OnboardingController.ts index 705531aad8..6329310a4f 100644 --- a/examples/alchemy-daapp/src/surfaces/onboarding/OnboardingController.ts +++ b/examples/alchemy-daapp/src/surfaces/onboarding/OnboardingController.ts @@ -23,18 +23,6 @@ import { metaForStepIdentifier, } from "./OnboardingDataModels"; -export enum GasFeeStrategy { - DEFAULT = "DEFAULT", - FIXED = "FIXED", - BASE_FEE_PERCENTAGE = "BASE_FEE_PERCENTAGE", - PRIORITY_FEE_PERCENTAGE = "PRIORITY_FEE_PERCENTAGE", -} - -export interface GasFeeMode { - strategy: GasFeeStrategy; - value: bigint; -} - async function pollForLambdaForComplete( lambda: () => Promise, txnMaxDurationSeconds: number = 20 diff --git a/packages/accounts/src/kernel-zerodev/e2e-tests/constants.ts b/packages/accounts/src/kernel-zerodev/e2e-tests/constants.ts index 59661e3288..4bda396c88 100644 --- a/packages/accounts/src/kernel-zerodev/e2e-tests/constants.ts +++ b/packages/accounts/src/kernel-zerodev/e2e-tests/constants.ts @@ -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!; diff --git a/packages/accounts/src/kernel-zerodev/e2e-tests/kernel-account.test.ts b/packages/accounts/src/kernel-zerodev/e2e-tests/kernel-account.test.ts index eb6061afbf..2cbc93a347 100644 --- a/packages/accounts/src/kernel-zerodev/e2e-tests/kernel-account.test.ts +++ b/packages/accounts/src/kernel-zerodev/e2e-tests/kernel-account.test.ts @@ -19,14 +19,14 @@ import { import { KernelAccountProvider } from "../provider.js"; import type { KernelUserOperationCallData } from "../types.js"; import { KernelBaseValidator, ValidatorMode } from "../validator/base.js"; -import { API_KEY, OWNER_MNEMONIC } from "./constants.js"; +import { RPC_URL, API_KEY, OWNER_MNEMONIC } from "./constants.js"; import { MockSigner } from "./mocks/mock-signer.js"; describe("Kernel Account Tests", () => { //any wallet should work const config = { chain: polygonMumbai, - rpcProvider: `${polygonMumbai.rpcUrls.alchemy.http[0]}/${API_KEY}`, + rpcProvider: RPC_URL !== undefined ? RPC_URL : `${polygonMumbai.rpcUrls.alchemy.http[0]}/${API_KEY}`, validatorAddress: "0x180D6465F921C7E0DEA0040107D342c87455fFF5" as Address, accountFactoryAddress: "0x5D006d3880645ec6e254E18C1F879DAC9Dd71A39" as Address, diff --git a/packages/alchemy/e2e-tests/constants.ts b/packages/alchemy/e2e-tests/constants.ts index bd350fcd31..e703ac40f9 100644 --- a/packages/alchemy/e2e-tests/constants.ts +++ b/packages/alchemy/e2e-tests/constants.ts @@ -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!; diff --git a/packages/alchemy/e2e-tests/simple-account.test.ts b/packages/alchemy/e2e-tests/simple-account.test.ts index f0a90d6aa5..df8ca57a8b 100644 --- a/packages/alchemy/e2e-tests/simple-account.test.ts +++ b/packages/alchemy/e2e-tests/simple-account.test.ts @@ -6,7 +6,7 @@ import { toHex, type Hash } from "viem"; import { mnemonicToAccount } from "viem/accounts"; import { polygonMumbai } from "viem/chains"; import { AlchemyProvider } from "../src/provider.js"; -import { API_KEY, OWNER_MNEMONIC, PAYMASTER_POLICY_ID } from "./constants.js"; +import { RPC_URL, API_KEY, OWNER_MNEMONIC, PAYMASTER_POLICY_ID } from "./constants.js"; const ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; const SIMPLE_ACCOUNT_FACTORY_ADDRESS = @@ -24,6 +24,7 @@ describe("Simple Account Tests", () => { const chain = polygonMumbai; const signer = new AlchemyProvider({ apiKey: API_KEY, + rpcUrl: RPC_URL, chain, entryPointAddress: ENTRYPOINT_ADDRESS, }).connect( @@ -57,6 +58,7 @@ describe("Simple Account Tests", () => { const accountAddress = "0xc33AbD9621834CA7c6Fc9f9CC3c47b9c17B03f9F"; const newSigner = new AlchemyProvider({ apiKey: API_KEY, + rpcUrl: RPC_URL, chain, entryPointAddress: ENTRYPOINT_ADDRESS, }).connect( diff --git a/packages/alchemy/src/chains.ts b/packages/alchemy/src/chains.ts index b55aac1433..acd1c3283c 100644 --- a/packages/alchemy/src/chains.ts +++ b/packages/alchemy/src/chains.ts @@ -10,7 +10,6 @@ import { polygonMumbai, sepolia, } from "viem/chains"; -import { GasFeeStrategy, type GasFeeMode } from "./middleware/gas-fees.js"; export const SupportedChains = new Map([ [polygonMumbai.id, polygonMumbai], @@ -23,56 +22,3 @@ export const SupportedChains = new Map([ [optimism.id, optimism], [optimismGoerli.id, optimismGoerli], ]); - -const defineChainStrategy = ( - chainId: number, - strategy: GasFeeStrategy, - value: GasFeeMode["value"] -): [number, GasFeeMode] => { - return [chainId, { strategy, value }]; -}; - -export const ChainFeeStrategies: Map = new Map< - number, - GasFeeMode ->([ - // testnets - defineChainStrategy( - goerli.id, - GasFeeStrategy.PRIORITY_FEE_INCREASE_PERCENT, - 0n - ), - defineChainStrategy( - sepolia.id, - GasFeeStrategy.PRIORITY_FEE_INCREASE_PERCENT, - 0n - ), - defineChainStrategy( - polygonMumbai.id, - GasFeeStrategy.PRIORITY_FEE_INCREASE_PERCENT, - 0n - ), - defineChainStrategy( - optimismGoerli.id, - GasFeeStrategy.PERCENT_OF_BASE_FEE, - 0n - ), - defineChainStrategy( - arbitrumGoerli.id, - GasFeeStrategy.PERCENT_OF_BASE_FEE, - 0n - ), - // mainnets - defineChainStrategy( - mainnet.id, - GasFeeStrategy.PRIORITY_FEE_INCREASE_PERCENT, - 57n - ), - defineChainStrategy( - polygon.id, - GasFeeStrategy.PRIORITY_FEE_INCREASE_PERCENT, - 25n - ), - defineChainStrategy(optimism.id, GasFeeStrategy.PERCENT_OF_BASE_FEE, 5n), - defineChainStrategy(arbitrum.id, GasFeeStrategy.PERCENT_OF_BASE_FEE, 5n), -]); diff --git a/packages/alchemy/src/index.ts b/packages/alchemy/src/index.ts index 62ee13c05d..794066eb98 100644 --- a/packages/alchemy/src/index.ts +++ b/packages/alchemy/src/index.ts @@ -1,8 +1,4 @@ -export { - GasFeeStrategy, - withAlchemyGasFeeEstimator, -} from "./middleware/gas-fees.js"; -export type { GasFeeMode } from "./middleware/gas-fees.js"; +export { withAlchemyGasFeeEstimator } from "./middleware/gas-fees.js"; export { withAlchemyGasManager, diff --git a/packages/alchemy/src/middleware/client.ts b/packages/alchemy/src/middleware/client.ts new file mode 100644 index 0000000000..dd64a9bee4 --- /dev/null +++ b/packages/alchemy/src/middleware/client.ts @@ -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"]; +}; diff --git a/packages/alchemy/src/middleware/gas-fees.ts b/packages/alchemy/src/middleware/gas-fees.ts index 0881e9b261..88eec9ca98 100644 --- a/packages/alchemy/src/middleware/gas-fees.ts +++ b/packages/alchemy/src/middleware/gas-fees.ts @@ -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 + ).request({ + method: "rundler_getLocalRequiredFees", + params: [], + }); + return result.recommended; }); return provider; }; diff --git a/packages/alchemy/src/middleware/gas-manager.ts b/packages/alchemy/src/middleware/gas-manager.ts index bc8b6752f3..ba0ad1abc9 100644 --- a/packages/alchemy/src/middleware/gas-manager.ts +++ b/packages/alchemy/src/middleware/gas-manager.ts @@ -3,43 +3,9 @@ import { resolveProperties, type ConnectedSmartAccountProvider, type PublicErc4337Client, - type UserOperationRequest, } from "@alchemy/aa-core"; -import type { Address, Hex, Transport } from "viem"; - -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"]; -}; +import type { Address, Transport } from "viem"; +import type { ClientWithAlchemyMethods } from "./client.js"; export interface AlchemyGasManagerConfig { policyId: string; diff --git a/packages/alchemy/src/provider.ts b/packages/alchemy/src/provider.ts index fcd5c9ec3c..775099c6fc 100644 --- a/packages/alchemy/src/provider.ts +++ b/packages/alchemy/src/provider.ts @@ -13,15 +13,12 @@ import { optimism, optimismGoerli, } from "viem/chains"; -import { ChainFeeStrategies, SupportedChains } from "./chains.js"; -import { - GasFeeStrategy, - withAlchemyGasFeeEstimator, -} from "./middleware/gas-fees.js"; +import { SupportedChains } from "./chains.js"; import { withAlchemyGasManager, type AlchemyGasManagerConfig, } from "./middleware/gas-manager.js"; +import { withAlchemyGasFeeEstimator } from "./middleware/gas-fees.js"; type ConnectionConfig = | { @@ -63,14 +60,7 @@ export class AlchemyProvider extends SmartAccountProvider { super(rpcUrl, entryPointAddress, _chain, account, opts); - withAlchemyGasFeeEstimator( - this, - ChainFeeStrategies.get(_chain.id) ?? { - strategy: GasFeeStrategy.DEFAULT, - value: 0n, - }, - feeOpts?.maxPriorityFeeBufferPercent ?? 5n - ); + withAlchemyGasFeeEstimator(this); } gasEstimator: AccountMiddlewareFn = async (struct) => { diff --git a/packages/core/e2e-tests/constants.ts b/packages/core/e2e-tests/constants.ts index 59661e3288..4bda396c88 100644 --- a/packages/core/e2e-tests/constants.ts +++ b/packages/core/e2e-tests/constants.ts @@ -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!; diff --git a/packages/core/e2e-tests/simple-account.test.ts b/packages/core/e2e-tests/simple-account.test.ts index 0dc4efd310..aee05a7390 100644 --- a/packages/core/e2e-tests/simple-account.test.ts +++ b/packages/core/e2e-tests/simple-account.test.ts @@ -7,7 +7,7 @@ import { } from "../src/account/simple.js"; import { SmartAccountProvider } from "../src/provider/base.js"; import { LocalAccountSigner } from "../src/signer/local-account.js"; -import { API_KEY, OWNER_MNEMONIC } from "./constants.js"; +import { RPC_URL, API_KEY, OWNER_MNEMONIC } from "./constants.js"; const ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; const SIMPLE_ACCOUNT_FACTORY_ADDRESS = @@ -18,7 +18,7 @@ describe("Simple Account Tests", () => { LocalAccountSigner.mnemonicToAccountSigner(OWNER_MNEMONIC); const chain = polygonMumbai; const signer = new SmartAccountProvider( - `${chain.rpcUrls.alchemy.http[0]}/${API_KEY}`, + RPC_URL !== undefined ? RPC_URL : `${chain.rpcUrls.alchemy.http[0]}/${API_KEY}`, ENTRYPOINT_ADDRESS, chain ).connect( @@ -51,7 +51,7 @@ describe("Simple Account Tests", () => { it("should fail to execute if account address is not deployed and not correct", async () => { const accountAddress = "0xc33AbD9621834CA7c6Fc9f9CC3c47b9c17B03f9F"; const newSigner = new SmartAccountProvider( - `${chain.rpcUrls.alchemy.http[0]}/${API_KEY}`, + RPC_URL !== undefined ? RPC_URL : `${chain.rpcUrls.alchemy.http[0]}/${API_KEY}`, ENTRYPOINT_ADDRESS, chain ).connect( @@ -79,7 +79,7 @@ describe("Simple Account Tests", () => { generatePrivateKey() ); const provider = new SmartAccountProvider( - `${chain.rpcUrls.alchemy.http[0]}/${API_KEY}`, + RPC_URL !== undefined ? RPC_URL : `${chain.rpcUrls.alchemy.http[0]}/${API_KEY}`, ENTRYPOINT_ADDRESS, chain ).connect( diff --git a/packages/core/src/provider/base.ts b/packages/core/src/provider/base.ts index 3b2e32e613..91a1384fb3 100644 --- a/packages/core/src/provider/base.ts +++ b/packages/core/src/provider/base.ts @@ -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, this.paymasterDataMiddleware, this.customMiddleware ?? noOpMiddleware, // This applies the overrides if they've been passed in diff --git a/packages/ethers/e2e-tests/constants.ts b/packages/ethers/e2e-tests/constants.ts index 59661e3288..4bda396c88 100644 --- a/packages/ethers/e2e-tests/constants.ts +++ b/packages/ethers/e2e-tests/constants.ts @@ -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!; diff --git a/packages/ethers/e2e-tests/simple-account.test.ts b/packages/ethers/e2e-tests/simple-account.test.ts index 851d99403a..6c34ab25e8 100644 --- a/packages/ethers/e2e-tests/simple-account.test.ts +++ b/packages/ethers/e2e-tests/simple-account.test.ts @@ -3,7 +3,7 @@ import { Wallet } from "@ethersproject/wallet"; import { Alchemy, Network } from "alchemy-sdk"; import { EthersProviderAdapter } from "../src/provider-adapter.js"; import { convertWalletToAccountSigner } from "../src/utils.js"; -import { API_KEY, OWNER_MNEMONIC } from "./constants.js"; +import { RPC_URL, API_KEY, OWNER_MNEMONIC } from "./constants.js"; const ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; const SIMPLE_ACCOUNT_FACTORY_ADDRESS = @@ -12,6 +12,7 @@ const SIMPLE_ACCOUNT_FACTORY_ADDRESS = describe("Simple Account Tests", async () => { const alchemy = new Alchemy({ apiKey: API_KEY, + url: RPC_URL, network: Network.MATIC_MUMBAI, }); const alchemyProvider = await alchemy.config.getProvider();