Skip to content

Commit

Permalink
feat: use alchemy_requestGasAndPaymasterAndData when using alchemy pa…
Browse files Browse the repository at this point in the history
…ymaster (#1310)

* feat: use alchemy_requestGasAndPaymasterAndData when using alchemy paymaster

* fix: accept transport param to use alchemy fee estimator & skip dummy middleware

* chore: fix typo

* fix: fall back to default dummy middleware when needed & simplify overrides

* chore: update comments & tests

* test: update tests for alchemy_requestGasAndPaymasterAndData

* chore: build docs

* fix: fix tests from merge conflict

* fix: fix docs example imports
  • Loading branch information
jakehobbs authored Feb 24, 2025
1 parent 6c2edde commit 20b80a6
Show file tree
Hide file tree
Showing 20 changed files with 714 additions and 54 deletions.
16 changes: 14 additions & 2 deletions .vitest/src/instances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const bundlerMethods = [
"debug_bundler_dumpMempool",
"debug_bundler_clearState",
"debug_bundler_setBundlingMode",
"rundler_maxPriorityFeePerGas",
];

function defineInstance(params: DefineInstanceParams) {
Expand Down Expand Up @@ -97,12 +98,23 @@ function defineInstance(params: DefineInstanceParams) {
transport: http(rpcUrls().bundler),
},
{
methods: ["pm_getPaymasterStubData", "pm_getPaymasterData"],
methods: [
"pm_getPaymasterStubData",
"pm_getPaymasterData",
"alchemy_requestGasAndPaymasterAndData",
],
transport: paymasterTransport(
createClient({
chain,
transport: http(rpcUrls().anvil),
}).extend(() => ({ mode: "anvil" }))
}).extend(() => ({ mode: "anvil" })),
// Paymaster transport needs to be able to send requests
// to the bundler in order to estimate gas during the
// alchemy_requestGasAndPaymasterAndData method.
createClient({
chain,
transport: http(rpcUrls().bundler),
}).extend(() => ({ mode: "bundler" }))
),
},
],
Expand Down
99 changes: 96 additions & 3 deletions .vitest/src/paymaster/transport.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,25 @@
import type { UserOperationRequest } from "@aa-sdk/core";
import { type Address, type Client, type Hex, custom } from "viem";
import {
type UserOperationRequest,
type UserOperationOverrides,
bigIntMultiply,
deepHexlify,
} from "@aa-sdk/core";
import {
type Address,
type Client,
type Hex,
custom,
hexToBigInt,
toHex,
} from "viem";
import { paymaster060 } from "./paymaster060";
import { paymaster070 } from "./paymaster070";
import { estimateUserOperationGas } from "../../../aa-sdk/core/src/actions/bundler/estimateUserOperationGas";

export const paymasterTransport = (client: Client & { mode: "anvil" }) =>
export const paymasterTransport = (
client: Client & { mode: "anvil" },
bundlerClient: Client & { mode: "bundler" }
) =>
custom({
request: async (args) => {
if (args.method === "pm_getPaymasterStubData") {
Expand Down Expand Up @@ -48,6 +64,83 @@ export const paymasterTransport = (client: Client & { mode: "anvil" }) =>
console.log(e);
throw e;
}
} else if (args.method === "alchemy_requestGasAndPaymasterAndData") {
try {
const [{ userOperation, entryPoint, overrides }] = args.params as [
{
policyId: string;
entryPoint: Address;
dummySignature: Hex;
userOperation: UserOperationRequest;
overrides?: UserOperationOverrides;
}
];
const isPMv7 =
entryPoint.toLowerCase() ===
paymaster070.entryPointAddress.toLowerCase();

let uo = { ...userOperation };

const maxFeePerGas: Hex = toHex(
bigIntMultiply(
hexToBigInt(
await client.request({
method: "eth_gasPrice",
})
),
1.5
)
);

const maxPriorityFeePerGas = await bundlerClient.request<{
Parameters: [];
ReturnType: UserOperationRequest["maxPriorityFeePerGas"];
}>({
method: "rundler_maxPriorityFeePerGas",
params: [],
});

const stubData = isPMv7
? paymaster070.getPaymasterStubData()
: paymaster060.getPaymasterStubData();

uo = {
...uo,
maxFeePerGas,
maxPriorityFeePerGas,
...stubData,
};

const gasEstimates = deepHexlify(
await estimateUserOperationGas(bundlerClient, {
request: uo,
entryPoint,
})
);

uo = {
...uo,
...gasEstimates,
...(isPMv7
? {
paymasterPostOpGasLimit: toHex(0),
}
: {}),
};

const pmFields = isPMv7
? await paymaster070.getPaymasterData(uo, client)
: await paymaster060.getPaymasterData(uo, client);

return {
...uo,
...pmFields,
...overrides,
};
} catch (err) {
console.log(err);
throw err;
}
}

throw new Error("Method not found");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ exports[`Alchemy Transport Tests > should correctly create a split transport 1`]
"rundler_maxPriorityFeePerGas",
"pm_getPaymasterData",
"pm_getPaymasterStubData",
"alchemy_requestGasAndPaymasterAndData",
],
"transport": [Function],
},
Expand All @@ -38,6 +39,7 @@ exports[`Alchemy Transport Tests > should correctly create a split transport 2`]
"rundler_maxPriorityFeePerGas",
"pm_getPaymasterData",
"pm_getPaymasterStubData",
"alchemy_requestGasAndPaymasterAndData",
],
"transport": [Function],
},
Expand All @@ -61,6 +63,7 @@ exports[`Alchemy Transport Tests > should correctly create a split transport 3`]
"rundler_maxPriorityFeePerGas",
"pm_getPaymasterData",
"pm_getPaymasterStubData",
"alchemy_requestGasAndPaymasterAndData",
],
"transport": [Function],
},
Expand All @@ -84,6 +87,7 @@ exports[`Alchemy Transport Tests > should correctly create a split transport 4`]
"rundler_maxPriorityFeePerGas",
"pm_getPaymasterData",
"pm_getPaymasterStubData",
"alchemy_requestGasAndPaymasterAndData",
],
"transport": [Function],
},
Expand Down
43 changes: 41 additions & 2 deletions account-kit/infra/src/actions/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import type { UserOperationStruct } from "@aa-sdk/core";
import type { Address, Hash } from "viem";
import type {
UserOperationStruct,
UserOperationRequest,
UserOperationOverrides,
EntryPointVersion,
} from "@aa-sdk/core";
import type { Address, Hash, Hex } from "viem";

export enum SimulateAssetType {
NATIVE = "NATIVE",
Expand Down Expand Up @@ -47,3 +52,37 @@ export interface SimulateAssetChange {
name?: string;
logo?: string;
}

export type RequestGasAndPaymasterAndDataRequest = [
{
policyId: string;
entryPoint: Address;
dummySignature: Hex;
userOperation: UserOperationRequest;
overrides?: UserOperationOverrides;
}
];

export type RequestGasAndPaymasterAndDataResponse<
TEntryPointVersion extends EntryPointVersion = EntryPointVersion
> = Pick<
UserOperationRequest,
| "callGasLimit"
| "preVerificationGas"
| "verificationGasLimit"
| "maxFeePerGas"
| "maxPriorityFeePerGas"
> &
(TEntryPointVersion extends "0.6.0"
? {
paymasterAndData: UserOperationRequest<"0.6.0">["paymasterAndData"];
}
: TEntryPointVersion extends "0.7.0"
? Pick<
UserOperationRequest<"0.7.0">,
| "paymaster"
| "paymasterData"
| "paymasterVerificationGasLimit"
| "paymasterPostOpGasLimit"
>
: never);
1 change: 1 addition & 0 deletions account-kit/infra/src/alchemyTransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const alchemyMethods = [
"rundler_maxPriorityFeePerGas",
"pm_getPaymasterData",
"pm_getPaymasterStubData",
"alchemy_requestGasAndPaymasterAndData",
];

export type AlchemyTransportConfig = (
Expand Down
15 changes: 11 additions & 4 deletions account-kit/infra/src/client/smartAccountClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { type Chain } from "viem";
import type { AlchemyTransport } from "../alchemyTransport.js";
import { getDefaultUserOperationFeeOptions } from "../defaults.js";
import { alchemyFeeEstimator } from "../middleware/feeEstimator.js";
import { alchemyGasManagerMiddleware } from "../middleware/gasManager.js";
import { alchemyGasAndPaymasterAndDataMiddleware } from "../middleware/gasManager.js";
import { alchemyUserOperationSimulator } from "../middleware/userOperationSimulator.js";
import {
alchemyActions,
Expand Down Expand Up @@ -153,18 +153,25 @@ export function createAlchemySmartAccountClient({
...opts,
feeOptions,
},
feeEstimator: feeEstimator ?? alchemyFeeEstimator(transport),
gasEstimator,
customMiddleware: async (struct, args) => {
if (isSmartAccountWithSigner(args.account)) {
transport.updateHeaders(getSignerTypeHeader(args.account));
}
return customMiddleware ? customMiddleware(struct, args) : struct;
},
feeEstimator: feeEstimator ?? alchemyFeeEstimator(transport),
...(policyId
? alchemyGasAndPaymasterAndDataMiddleware({
policyId,
transport,
gasEstimatorOverride: gasEstimator,
feeEstimatorOverride: feeEstimator,
})
: {}),
userOperationSimulator: useSimulation
? alchemyUserOperationSimulator(transport)
: undefined,
gasEstimator,
...(policyId && alchemyGasManagerMiddleware(policyId)),
signUserOperation,
}).extend(alchemyActions);

Expand Down
14 changes: 13 additions & 1 deletion account-kit/infra/src/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
import type {
SimulateUserOperationAssetChangesRequest,
SimulateUserOperationAssetChangesResponse,
RequestGasAndPaymasterAndDataRequest,
RequestGasAndPaymasterAndDataResponse,
} from "../actions/types";
import type { AlchemyTransport } from "../alchemyTransport";

Expand All @@ -20,7 +22,12 @@ export type AlchemyRpcSchema = [
Parameters: [];
ReturnType: UserOperationRequest["maxPriorityFeePerGas"];
},
...Erc7677RpcSchema<{ policyId: string }>
...Erc7677RpcSchema<{ policyId: string }>,
{
Method: "alchemy_requestGasAndPaymasterAndData";
Parameters: RequestGasAndPaymasterAndDataRequest;
ReturnType: RequestGasAndPaymasterAndDataResponse;
}
];

export type ClientWithAlchemyMethods = BundlerClient<AlchemyTransport> & {
Expand All @@ -35,5 +42,10 @@ export type ClientWithAlchemyMethods = BundlerClient<AlchemyTransport> & {
method: "rundler_maxPriorityFeePerGas";
params: [];
}): Promise<UserOperationRequest["maxPriorityFeePerGas"]>;

request(args: {
method: "alchemy_requestGasAndPaymasterAndData";
params: RequestGasAndPaymasterAndDataRequest;
}): Promise<RequestGasAndPaymasterAndDataResponse>;
}["request"];
};
2 changes: 2 additions & 0 deletions account-kit/infra/src/gas-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const AlchemyPaymasterAddressV1 =
*
* @param {Chain} chain The chain for which the paymaster address is required
* @returns {Address} The Alchemy paymaster address corresponding to the specified chain
*
* @deprecated This chain list in this function is no longer maintained since the ERC-7677 middleware is typically used to resolve the paymaster address
*/
export const getAlchemyPaymasterAddress = (chain: Chain): Address => {
switch (chain.id) {
Expand Down
5 changes: 4 additions & 1 deletion account-kit/infra/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export { getDefaultUserOperationFeeOptions } from "./defaults.js";
export { getAlchemyPaymasterAddress } from "./gas-manager.js";
export { alchemyFeeEstimator } from "./middleware/feeEstimator.js";
export type * from "./middleware/gasManager.js";
export { alchemyGasManagerMiddleware } from "./middleware/gasManager.js";
export {
alchemyGasManagerMiddleware,
alchemyGasAndPaymasterAndDataMiddleware,
} from "./middleware/gasManager.js";
export { alchemyUserOperationSimulator } from "./middleware/userOperationSimulator.js";
export type * from "./schema.js";
Loading

0 comments on commit 20b80a6

Please sign in to comment.