Skip to content

Commit

Permalink
feat: estimate gas fees for user ops with `eth_estimateUserOperationG…
Browse files Browse the repository at this point in the history
…as` (#48)

Co-authored-by: bigq <[email protected]>
  • Loading branch information
yum0e and yum0e authored Nov 23, 2023
1 parent 9dfb58f commit 0ccff7a
Show file tree
Hide file tree
Showing 8 changed files with 33 additions and 229 deletions.
2 changes: 0 additions & 2 deletions front/src/components/NavBar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import { Button, Flex } from "@radix-ui/themes";
import { useModal } from "@/providers/ModalProvider";
import { PaperPlaneIcon, CornersIcon } from "@radix-ui/react-icons";
import { SendTransaction } from "@/components/SendTransaction";
import { useEffect } from "react";
import QrReaderModal from "../QrReaderModal";
import SendTxModal from "../SendTxModal";

Expand Down
213 changes: 0 additions & 213 deletions front/src/components/SendTransaction.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion front/src/components/SendTxModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export default function SendTxModal() {
dest: destination.toLowerCase() as Hex,
value:
(BigInt(userInputAmount) * BigInt(1e18)) /
(BigInt(price.ethereum.usd * 100) / BigInt(100)), // 100 is the price precision
(BigInt(Math.trunc(price.ethereum.usd * 100)) / BigInt(100)), // 100 is the price precision
data: emptyHex,
},
],
Expand Down
1 change: 0 additions & 1 deletion front/src/components/WCSendTransactionModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export default function WCSendTransactionModal({ params }: Props) {
data: params.data,
},
],
callGasLimit,
maxFeePerGas: maxFeePerGas as bigint,
maxPriorityFeePerGas: maxPriorityFeePerGas as bigint,
keyId: me.keyId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { ENTRYPOINT_ADDRESS } from "@/constants";
import { SmartWalletClient } from "@/libs/smart-wallet/service/smart-wallet";
import { UserOperationAsHex } from "@/libs/smart-wallet/service/userOps";

/* */
export type EstimateUserOperationGasReturnType = bigint;
export type EstimateUserOperationGasReturnType = {
preVerificationGas: bigint;
verificationGasLimit: bigint;
callGasLimit: bigint;
};

export async function estimateUserOperationGas(
client: SmartWalletClient,
args: any,
args: { userOp: UserOperationAsHex },
): Promise<EstimateUserOperationGasReturnType> {
return await client.request({
method: "eth_estimateUserOperationGas" as any,
params: args,
params: [{ ...args.userOp }, ENTRYPOINT_ADDRESS],
});
}
4 changes: 3 additions & 1 deletion front/src/libs/smart-wallet/service/decorators/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import { UserOperationAsHex } from "@/libs/smart-wallet/service/userOps";

export type SmartWalletActions = {
sendUserOperation: (args: { userOp: UserOperationAsHex }) => Promise<SendUserOperationReturnType>;
estimateUserOperationGas: (args: any) => Promise<EstimateUserOperationGasReturnType>;
estimateUserOperationGas: (args: {
userOp: UserOperationAsHex;
}) => Promise<EstimateUserOperationGasReturnType>;
getUserOperationReceipt: (args: any) => Promise<GetUserOperationReceiptReturnType>;
getIsValidSignature: (args: any) => Promise<GetIsValidSignatureReturnType>;
waitForUserOperationReceipt: (args: any) => Promise<GetUserOperationReceiptReturnType>;
Expand Down
5 changes: 4 additions & 1 deletion front/src/libs/smart-wallet/service/smart-wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SmartWalletActions, smartWalletActions } from "./decorators";
import { transport } from "../config";
import { ERC4337RpcSchema, UserOperationAsHex } from "@/libs/smart-wallet/service/userOps";
import { CHAIN } from "@/constants";
import { EstimateUserOperationGasReturnType } from "@/libs/smart-wallet/service/actions";

export type SmartWalletClient<chain extends Chain | undefined = Chain | undefined> = Client<
Transport,
Expand Down Expand Up @@ -61,7 +62,9 @@ class SmartWallet {
});
}

public async estimateUserOperationGas(args: any): Promise<bigint> {
public async estimateUserOperationGas(args: {
userOp: UserOperationAsHex;
}): Promise<EstimateUserOperationGasReturnType> {
this._isInit();
return await this._client.estimateUserOperationGas({
...args,
Expand Down
23 changes: 16 additions & 7 deletions front/src/libs/smart-wallet/service/userOps/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { UserOperationAsHex, UserOperation, Call } from "@/libs/smart-wallet/ser
import { DEFAULT_USER_OP } from "@/libs/smart-wallet/service/userOps/constants";
import { P256Credential, WebAuthn } from "@/libs/web-authn";
import { ENTRYPOINT_ABI, ENTRYPOINT_ADDRESS, FACTORY_ABI } from "@/constants";
import { smartWallet } from "@/libs/smart-wallet";

export class UserOpBuilder {
public relayer: Hex = "0x061060a65146b3265C62fC8f3AE977c9B27260fF";
Expand Down Expand Up @@ -54,16 +55,14 @@ export class UserOpBuilder {
calls,
maxFeePerGas,
maxPriorityFeePerGas,
callGasLimit,
keyId,
}: {
calls: Call[];
maxFeePerGas: bigint;
maxPriorityFeePerGas: bigint;
callGasLimit?: bigint;
keyId: Hex;
}): Promise<UserOperationAsHex> {
// calculate smart wallet address via Factory contract
// calculate smart wallet address via Factory contract to know the sender
const { account, publicKey } = await this._calculateSmartWalletAddress(keyId); // the keyId is the id tied to the user's public key

// get bytecode
Expand Down Expand Up @@ -94,12 +93,22 @@ export class UserOpBuilder {
callData,
maxFeePerGas,
maxPriorityFeePerGas,
callGasLimit: callGasLimit ?? BigInt(18286) * BigInt(2),
preVerificationGas: BigInt(57705) * BigInt(10),
verificationGasLimit:
BigInt(97655) + BigInt(150_000) + BigInt(initCodeGas) + BigInt(2_000_000),
};

// estimate gas for this partial user operation
// real good article about the subject can be found here:
// https://www.alchemy.com/blog/erc-4337-gas-estimation
const { callGasLimit, verificationGasLimit, preVerificationGas } =
await smartWallet.estimateUserOperationGas({
userOp: this.toParams(userOp),
});

// set gas limits with the estimated values + some extra gas for safety
userOp.callGasLimit = callGasLimit;
userOp.preVerificationGas = BigInt(preVerificationGas) * BigInt(10);
userOp.verificationGasLimit =
BigInt(verificationGasLimit) + BigInt(150_000) + BigInt(initCodeGas) + BigInt(1_000_000);

// get userOp hash (with signature == 0x) by calling the entry point contract
const userOpHash = await this._getUserOpHash(userOp);

Expand Down

0 comments on commit 0ccff7a

Please sign in to comment.