Skip to content

Commit 0f5c75b

Browse files
alvrsfrolic
andauthored
feat(entrykit): add wiresaw transport (#3703)
Co-authored-by: Kevin Ingersoll <kingersoll@gmail.com>
1 parent 6508c1d commit 0f5c75b

15 files changed

Lines changed: 502 additions & 50 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@latticexyz/entrykit": patch
3+
---
4+
5+
Added experimental support for fast user operations on Wiresaw-enabled chains.

packages/common/src/exports/internal.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export * from "../deploy/ensureContractsDeployed";
44
export * from "../deploy/ensureDeployer";
55
export * from "../deploy/getContractAddress";
66
export * from "../deploy/getDeployer";
7+
export * from "../transports/wiresaw";
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { Chain, fallback, http, Transport, webSocket } from "viem";
2+
3+
export function chainTransport(rpcUrls: Chain["rpcUrls"][string]): Transport | undefined {
4+
const webSocketUrl = rpcUrls?.webSocket?.[0];
5+
const httpUrl = rpcUrls?.http[0];
6+
7+
if (webSocketUrl) {
8+
return httpUrl ? fallback([webSocket(webSocketUrl), http(httpUrl)]) : webSocket(webSocketUrl);
9+
}
10+
11+
if (httpUrl) {
12+
return http(httpUrl);
13+
}
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { RpcSchema, UnionToTuple } from "viem";
2+
3+
export type getRpcMethod<rpcSchema extends RpcSchema, method extends rpcSchema[number]["Method"]> = Extract<
4+
rpcSchema[number],
5+
{ Method: method }
6+
>;
7+
8+
export type getRpcSchema<rpcSchema extends RpcSchema, method extends rpcSchema[number]["Method"]> = UnionToTuple<
9+
getRpcMethod<rpcSchema, method>
10+
>;
11+
12+
export type getRpcReturnType<rpcSchema extends RpcSchema, method extends rpcSchema[number]["Method"]> = {
13+
[k in keyof rpcSchema & number as rpcSchema[k]["Method"]]: rpcSchema[k]["ReturnType"];
14+
}[method];
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
export const entryPointGasSimulationsAbi = [
2+
{
3+
inputs: [
4+
{
5+
components: [
6+
{
7+
internalType: "address",
8+
name: "sender",
9+
type: "address",
10+
},
11+
{
12+
internalType: "uint256",
13+
name: "nonce",
14+
type: "uint256",
15+
},
16+
{
17+
internalType: "bytes",
18+
name: "initCode",
19+
type: "bytes",
20+
},
21+
{
22+
internalType: "bytes",
23+
name: "callData",
24+
type: "bytes",
25+
},
26+
{
27+
internalType: "bytes32",
28+
name: "accountGasLimits",
29+
type: "bytes32",
30+
},
31+
{
32+
internalType: "uint256",
33+
name: "preVerificationGas",
34+
type: "uint256",
35+
},
36+
{
37+
internalType: "bytes32",
38+
name: "gasFees",
39+
type: "bytes32",
40+
},
41+
{
42+
internalType: "bytes",
43+
name: "paymasterAndData",
44+
type: "bytes",
45+
},
46+
{
47+
internalType: "bytes",
48+
name: "signature",
49+
type: "bytes",
50+
},
51+
],
52+
internalType: "struct PackedUserOperation",
53+
name: "op",
54+
type: "tuple",
55+
},
56+
],
57+
name: "estimateGas",
58+
outputs: [
59+
{
60+
components: [
61+
{
62+
internalType: "uint256",
63+
name: "verificationGas",
64+
type: "uint256",
65+
},
66+
{
67+
internalType: "uint256",
68+
name: "callGas",
69+
type: "uint256",
70+
},
71+
{
72+
internalType: "uint256",
73+
name: "paymasterVerificationGas",
74+
type: "uint256",
75+
},
76+
{
77+
internalType: "uint256",
78+
name: "paymasterPostOpGas",
79+
type: "uint256",
80+
},
81+
],
82+
internalType: "struct EntryPoint.GasInfo",
83+
name: "",
84+
type: "tuple",
85+
},
86+
],
87+
stateMutability: "nonpayable",
88+
type: "function",
89+
},
90+
] as const;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import {
2+
BundlerRpcSchema,
3+
decodeFunctionResult,
4+
DecodeFunctionResultReturnType,
5+
EIP1193RequestFn,
6+
encodeFunctionData,
7+
Hex,
8+
zeroAddress,
9+
} from "viem";
10+
import { getRpcMethod } from "../common";
11+
import {
12+
entryPoint07Address,
13+
formatUserOperation,
14+
formatUserOperationRequest,
15+
toPackedUserOperation,
16+
UserOperation,
17+
} from "viem/account-abstraction";
18+
import { bigIntMax } from "../../utils";
19+
import { entryPointGasSimulationsAbi } from "../entryPointGasSimulations";
20+
21+
type rpcMethod = getRpcMethod<BundlerRpcSchema, "eth_estimateUserOperationGas">;
22+
23+
type EstimateUserOperationGasOptions = {
24+
request: EIP1193RequestFn;
25+
params: rpcMethod["Parameters"];
26+
};
27+
28+
export async function estimateUserOperationGas({
29+
request,
30+
params,
31+
}: EstimateUserOperationGasOptions): Promise<rpcMethod["ReturnType"]> {
32+
const userOp = formatUserOperation(params[0]);
33+
const gasSimulation = await simulateGas({ userOp, request });
34+
const gasLimits = {
35+
verificationGasLimit: gasSimulation.verificationGas * 2n,
36+
callGasLimit: bigIntMax(gasSimulation.callGas * 2n, 9000n),
37+
paymasterVerificationGasLimit: gasSimulation.paymasterVerificationGas * 2n,
38+
paymasterPostOpGasLimit: gasSimulation.paymasterPostOpGas * 2n,
39+
preVerificationGas: 20_000n,
40+
};
41+
42+
return formatUserOperationRequest({
43+
...gasLimits,
44+
});
45+
}
46+
47+
type SimulateGasOptions = {
48+
request: EIP1193RequestFn;
49+
userOp: UserOperation<"0.7">;
50+
};
51+
52+
type SimulateGasResult = DecodeFunctionResultReturnType<typeof entryPointGasSimulationsAbi>;
53+
54+
async function simulateGas({ request, userOp }: SimulateGasOptions): Promise<SimulateGasResult> {
55+
// Prepare user operation for simulation
56+
const simulationUserOp = {
57+
...userOp,
58+
preVerificationGas: 0n,
59+
callGasLimit: 10_000_000n,
60+
verificationGasLimit: 10_000_000n,
61+
// https://github.com/pimlicolabs/alto/blob/471998695e5ec75ef88dda3f8a534f47c24bcd1a/src/rpc/methods/eth_estimateUserOperationGas.ts#L117
62+
maxPriorityFeePerGas: userOp.maxFeePerGas,
63+
paymasterPostOpGasLimit: 2_000_000n,
64+
paymasterVerificationGasLimit: 5_000_000n,
65+
} satisfies UserOperation<"0.7">;
66+
67+
const packedUserOp = toPackedUserOperation(simulationUserOp);
68+
const simulationData = encodeFunctionData({
69+
abi: entryPointGasSimulationsAbi,
70+
functionName: "estimateGas",
71+
args: [packedUserOp],
72+
});
73+
74+
const hasPaymaster = userOp.paymaster != null && userOp.paymaster !== zeroAddress;
75+
const senderBalanceOverride = hasPaymaster ? {} : { [userOp.sender]: { balance: "0xFFFFFFFFFFFFFFFFFFFF" } };
76+
const simulationParams = [
77+
{
78+
to: entryPoint07Address,
79+
data: simulationData,
80+
},
81+
"pending",
82+
{
83+
...senderBalanceOverride,
84+
},
85+
];
86+
const encodedSimulationResult: Hex = await request({
87+
method: "wiresaw_callEntryPointSimulations",
88+
params: simulationParams,
89+
});
90+
91+
return decodeFunctionResult({
92+
abi: entryPointGasSimulationsAbi,
93+
functionName: "estimateGas",
94+
data: encodedSimulationResult,
95+
});
96+
}

packages/entrykit/src/quarry/transports/methods/getUserOperationReceipt.ts renamed to packages/common/src/transports/methods/getUserOperationReceipt.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@ import {
1212
} from "viem";
1313
import { entryPoint07Abi } from "viem/account-abstraction";
1414

15-
// TODO: move to common package?
16-
1715
const userOperationRevertReasonAbi = [
1816
entryPoint07Abi.find(
1917
(item): item is ExtractAbiItem<typeof entryPoint07Abi, "UserOperationRevertReason"> =>

0 commit comments

Comments
 (0)