Skip to content
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

add simulation type param #304

Merged
merged 12 commits into from
Sep 28, 2023
4 changes: 3 additions & 1 deletion packages/account/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@
"@biconomy/node-client": "^3.1.0",
"@biconomy/paymaster": "^3.1.0",
"@ethersproject/providers": "^5.7.2",
"ethers": "^5.7.0"
"ethers": "^5.7.0",
"loglevel": "^1.8.1",
"lru-cache": "^10.0.1"
}
}
16 changes: 13 additions & 3 deletions packages/account/src/BaseSmartAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
import { NotPromise, packUserOp, Logger, RPC_PROVIDER_URLS } from "@biconomy/common";
import { IBundler, UserOpResponse } from "@biconomy/bundler";
import { IPaymaster, PaymasterAndDataResponse } from "@biconomy/paymaster";
import { BaseSmartAccountConfig, Overrides, TransactionDetailsForUserOp } from "./utils/Types";
import { BaseSmartAccountConfig, Overrides, SendUserOpOptions, TransactionDetailsForUserOp } from "./utils/Types";
import { GasOverheads } from "./utils/Preverificaiton";
import { EntryPoint, EntryPoint__factory } from "@account-abstraction/contracts";
import { DEFAULT_ENTRYPOINT_ADDRESS } from "./utils/Constants";
import { LRUCache } from 'lru-cache'

Check failure on line 14 in packages/account/src/BaseSmartAccount.ts

View workflow job for this annotation

GitHub Actions / Lint sources (18.x)

Replace `'lru-cache'` with `"lru-cache";`

type UserOperationKey = keyof UserOperation;

Expand All @@ -35,6 +36,10 @@
// entryPoint connected to "zero" address. allowed to make static calls (e.g. to getSenderAddress)
private readonly entryPoint!: EntryPoint;

private isContractDeployedCache = new LRUCache({
max: 500,
});

constructor(_smartAccountConfig: BaseSmartAccountConfig) {
this.index = _smartAccountConfig.index ?? 0;
this.overheads = _smartAccountConfig.overheads;
Expand Down Expand Up @@ -176,7 +181,7 @@
* @description This function call will take 'signedUserOp' as input and send it to the bundler
* @returns
*/
async sendSignedUserOp(userOp: UserOperation): Promise<UserOpResponse> {
async sendSignedUserOp(userOp: UserOperation, params?: SendUserOpOptions): Promise<UserOpResponse> {
const requiredFields: UserOperationKey[] = [
"sender",
"nonce",
Expand All @@ -194,7 +199,7 @@
Logger.log("userOp validated");
if (!this.bundler) throw new Error("Bundler is not provided");
Logger.log("userOp being sent to the bundler", userOp);
const bundlerResponse = await this.bundler.sendUserOp(userOp);
const bundlerResponse = await this.bundler.sendUserOp(userOp, params);
return bundlerResponse;
}

Expand Down Expand Up @@ -270,10 +275,15 @@
}

async isAccountDeployed(address: string): Promise<boolean> {
if (this.isContractDeployedCache.get(address)) {
return true;
}

this.isProviderDefined();
let isDeployed = false;
const contractCode = await this.provider.getCode(address);
if (contractCode.length > 2) {
this.isContractDeployedCache.set(address, true);
isDeployed = true;
} else {
isDeployed = false;
Expand Down
28 changes: 19 additions & 9 deletions packages/account/src/BiconomySmartAccountV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ import {
SmartAccount_v200__factory,
SmartAccountFactory_v200__factory,
} from "@biconomy/common";
import { BiconomyTokenPaymasterRequest, BiconomySmartAccountV2Config, CounterFactualAddressParam, BuildUserOpOptions } from "./utils/Types";
import { BaseValidationModule, ModuleInfo } from "@biconomy/modules";
import {
BiconomyTokenPaymasterRequest,
BiconomySmartAccountV2Config,
CounterFactualAddressParam,
BuildUserOpOptions,
SendUserOpOptions,
} from "./utils/Types";
import { BaseValidationModule, ModuleInfo, SendUserOpParams } from "@biconomy/modules";
import { UserOperation, Transaction } from "@biconomy/core-types";
import NodeClient from "@biconomy/node-client";
import INodeClient from "@biconomy/node-client";
Expand All @@ -32,6 +38,7 @@ import {
DEFAULT_FALLBACK_HANDLER_ADDRESS,
PROXY_CREATION_CODE,
} from "./utils/Constants";
import log from "loglevel";

type UserOperationKey = keyof UserOperation;
export class BiconomySmartAccountV2 extends BaseSmartAccount {
Expand Down Expand Up @@ -130,11 +137,14 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount {
// Could call it nonce space
async getNonce(nonceKey?: number): Promise<BigNumber> {
const nonceSpace = nonceKey ?? 0;
if (await this.isAccountDeployed(await this.getAccountAddress())) {
try {
const accountContract = await this._getAccountContract();
return accountContract.nonce(nonceSpace);
const nonce = await accountContract.nonce(nonceSpace);
return nonce;
} catch (e) {
log.debug("Failed to get nonce from deployed account. Returning 0 as nonce");
return BigNumber.from(0);
}
return BigNumber.from(0);
}

/**
Expand Down Expand Up @@ -240,7 +250,7 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount {
return "0x";
}

async signUserOp(userOp: Partial<UserOperation>, params?: ModuleInfo): Promise<UserOperation> {
async signUserOp(userOp: Partial<UserOperation>, params?: SendUserOpParams): Promise<UserOperation> {
this.isActiveValidationModuleDefined();
const requiredFields: UserOperationKey[] = [
"sender",
Expand Down Expand Up @@ -296,11 +306,11 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount {
* @description This function call will take 'unsignedUserOp' as an input, sign it with the owner key, and send it to the bundler.
* @returns Promise<UserOpResponse>
*/
async sendUserOp(userOp: Partial<UserOperation>, params?: ModuleInfo): Promise<UserOpResponse> {
async sendUserOp(userOp: Partial<UserOperation>, params?: SendUserOpOptions): Promise<UserOpResponse> {
Logger.log("userOp received in base account ", userOp);
delete userOp.signature;
const userOperation = await this.signUserOp(userOp, params);
const bundlerResponse = await this.sendSignedUserOp(userOperation);
const bundlerResponse = await this.sendSignedUserOp(userOperation, params);
return bundlerResponse;
}

Expand Down Expand Up @@ -486,7 +496,7 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount {
return userOp;
}

async signUserOpHash(userOpHash: string, params?: ModuleInfo): Promise<string> {
async signUserOpHash(userOpHash: string, params?: SendUserOpParams): Promise<string> {
this.isActiveValidationModuleDefined();
const moduleSig = await this.activeValidationModule.signUserOpHash(userOpHash, params);

Expand Down
10 changes: 5 additions & 5 deletions packages/account/src/SmartAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { IBundler, UserOpResponse } from "@biconomy/bundler";
import { IPaymaster, PaymasterAndDataResponse } from "@biconomy/paymaster";
import { Logger } from "@biconomy/common";
import { IEntryPoint } from "@account-abstraction/contracts";
import { SmartAccountConfig, Overrides } from "./utils/Types";
import { SmartAccountConfig, Overrides, SendUserOpDto } from "./utils/Types";

type UserOperationKey = keyof UserOperation;

Expand Down Expand Up @@ -239,11 +239,11 @@ export abstract class SmartAccount implements ISmartAccount {
* @description This function call will take 'unsignedUserOp' as an input, sign it with the owner key, and send it to the bundler.
* @returns Promise<UserOpResponse>
*/
async sendUserOp(userOp: Partial<UserOperation>): Promise<UserOpResponse> {
async sendUserOp(userOp: Partial<UserOperation>, params?: SendUserOpDto): Promise<UserOpResponse> {
Logger.log("userOp received in base account ", userOp);
delete userOp.signature;
const userOperation = await this.signUserOp(userOp);
const bundlerResponse = await this.sendSignedUserOp(userOperation);
const bundlerResponse = await this.sendSignedUserOp(userOperation, params);
return bundlerResponse;
}

Expand All @@ -253,7 +253,7 @@ export abstract class SmartAccount implements ISmartAccount {
* @description This function call will take 'signedUserOp' as input and send it to the bundler
* @returns
*/
async sendSignedUserOp(userOp: UserOperation): Promise<UserOpResponse> {
async sendSignedUserOp(userOp: UserOperation, params?: SendUserOpDto): Promise<UserOpResponse> {
const requiredFields: UserOperationKey[] = [
"sender",
"nonce",
Expand All @@ -271,7 +271,7 @@ export abstract class SmartAccount implements ISmartAccount {
Logger.log("userOp validated");
if (!this.bundler) throw new Error("Bundler is not provided");
Logger.log("userOp being sent to the bundler", userOp);
const bundlerResponse = await this.bundler.sendUserOp(userOp);
const bundlerResponse = await this.bundler.sendUserOp(userOp, params);
return bundlerResponse;
}
}
13 changes: 13 additions & 0 deletions packages/account/src/utils/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ export type NonceOptions = {
nonceOverride?: number;
};

// Used in AccountV1
export type SendUserOpDto = {
signer?: Signer;
simulationType?: SimulationType;
};

// Generic options in AccountV2
export type SendUserOpOptions = {
simulationType?: SimulationType;
};

export type SimulationType = "validation" | "validation_and_execution";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make this proper enums? and options as all uppercase

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Want to avoid the enums as it makes the code large, client have to import the enum from types and then pass it EnumName.Value, meanwhile this will be autofill in string. The values are also not that large, just two possible cases.


export type Overrides = {
callGasLimit?: BigNumberish;
verificationGasLimit?: BigNumberish;
Expand Down
8 changes: 6 additions & 2 deletions packages/bundler/src/Bundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
SendUserOpResponse,
UserOpGasResponse,
UserOpByHashResponse,
SendUserOpOptions,
} from "./utils/Types";
import { resolveProperties } from "ethers/lib/utils";
import { deepHexlify, sendRequest, getTimestampInSeconds, HttpMethod, Logger, RPC_PROVIDER_URLS } from "@biconomy/common";
Expand Down Expand Up @@ -78,12 +79,15 @@ export class Bundler implements IBundler {
* @description This function will send signed userOp to bundler to get mined on chain
* @returns Promise<UserOpResponse>
*/
async sendUserOp(userOp: UserOperation): Promise<UserOpResponse> {
async sendUserOp(userOp: UserOperation, simulationParam?: SendUserOpOptions): Promise<UserOpResponse> {
const chainId = this.bundlerConfig.chainId;
// transformUserOP will convert all bigNumber values to string
userOp = transformUserOP(userOp);
const hexifiedUserOp = deepHexlify(await resolveProperties(userOp));
const params = [hexifiedUserOp, this.bundlerConfig.entryPointAddress];
const simType = {
simulation_type: simulationParam?.simulationType || "validation",
};
const params = [hexifiedUserOp, this.bundlerConfig.entryPointAddress, simType];
const bundlerUrl = this.getBundlerUrl();
const sendUserOperationResponse: SendUserOpResponse = await sendRequest({
url: bundlerUrl,
Expand Down
4 changes: 2 additions & 2 deletions packages/bundler/src/interfaces/IBundler.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { UserOpResponse, UserOpGasResponse, UserOpReceipt, UserOpByHashResponse } from "../utils/Types";
import { UserOpResponse, UserOpGasResponse, UserOpReceipt, UserOpByHashResponse, SendUserOpOptions } from "../utils/Types";
import { UserOperation } from "@biconomy/core-types";

export interface IBundler {
estimateUserOpGas(_userOp: Partial<UserOperation>): Promise<UserOpGasResponse>;
sendUserOp(_userOp: UserOperation): Promise<UserOpResponse>;
sendUserOp(_userOp: UserOperation, _simulationParam?: SendUserOpOptions): Promise<UserOpResponse>;
getUserOpReceipt(_userOpHash: string): Promise<UserOpReceipt>;
getUserOpByHash(_userOpHash: string): Promise<UserOpByHashResponse>;
}
6 changes: 6 additions & 0 deletions packages/bundler/src/utils/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export type UserOpReceipt = {
receipt: ethers.providers.TransactionReceipt;
};

export type SendUserOpOptions = {
simulationType?: SimulationType;
};

export type SimulationType = "validation" | "validation_and_execution";

// Converted to JsonRpcResponse with strict type
export type GetUserOperationResponse = {
jsonrpc: string;
Expand Down
6 changes: 6 additions & 0 deletions packages/modules/src/utils/Types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ export type ModuleInfo = {
batchSessionParams?: SessionParams[];
};

export interface SendUserOpParams extends ModuleInfo {
simulationType?: SimulationType;
}

export type SimulationType = "validation" | "validation_and_execution";

export type CreateSessionDataResponse = {
data: string;
sessionIDInfo: Array<string>;
Expand Down
2 changes: 1 addition & 1 deletion packages/node-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ const balanceParams: BalancesDto =
tokenAddresses: [],
};

const balFromSdk = await nodeClient.getAlltokenBalances(balanceParams);
const balFromSdk = await nodeClient.getAllTokenBalances(balanceParams);
console.info("balFromSdk ", balFromSdk);

const usdBalFromSdk = await nodeClient.getTotalBalanceInUsd(balanceParams);
Expand Down
Loading