Skip to content

Commit

Permalink
Merge pull request #345 from bcnmy/release/r7
Browse files Browse the repository at this point in the history
Release/r7
  • Loading branch information
livingrockrises authored Jan 2, 2024
2 parents f74b117 + 1868d71 commit 7a165b0
Show file tree
Hide file tree
Showing 38 changed files with 745 additions and 108 deletions.
2 changes: 2 additions & 0 deletions linkAll.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
!/bin/sh
for dir in ./packages/*; do (cd "$dir" && yarn link); done
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"author": "Biconomy (https://biconomy.io)",
"private": true,
"scripts": {
"dev": "yarn rebuild && yarn install && yarn build && yarn link:all",
"rebuild": "./rebuild.sh",
"link:all": "./linkAll.sh",
"build": "lerna run build",
"clean": "lerna clean && lerna run unbuild",
"format": "lerna run format --npm-client=yarn",
Expand Down Expand Up @@ -56,7 +59,6 @@
"typescript": "^5.2.2"
},
"devDependencies": {
"ganache": "^7.9.1",
"@types/debug": "^4.1.9",
"@types/jest": "^29.5.4",
"@typescript-eslint/eslint-plugin": "^6.7.0",
Expand All @@ -68,6 +70,7 @@
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-security": "^1.7.1",
"ganache": "^7.9.1",
"hardhat": "^2.17.3",
"jest": "^29.7.0",
"lerna": "^7.2.0",
Expand Down
15 changes: 13 additions & 2 deletions packages/account/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

## 3.1.2 (2023-12-28)

### Features

* Make entryPointAddress optional in config([cf35c4a](https://github.com/bcnmy/biconomy-client-sdk/pull/336/commits/cf35c4a8266d27648035d8c9d63f1b9157553128))

### Bug Fixes

* use undefined in place of ! + check on limits returned by paymaster and throw ([0376901](https://github.com/bcnmy/biconomy-client-sdk/commit/0376901b7aec8c268a6a3c654d147335974d78f3))
* change receipt status type from boolean to string to be compatible with bundler response. ([317f986](https://github.com/bcnmy/biconomy-client-sdk/pull/342/commits/317f986b7e8f08d3ccf1e68f12a0696f1116de6b))

## 3.1.1 (2023-11-09)


Expand Down Expand Up @@ -47,7 +58,7 @@ Modular Account Abstraction is here. Contains BiconomySmartAccountV2 - an API fo

## 3.0.0 (2023-08-28)

VERSION bump only
VERSION Bump Only.

Modular SDK - consists stable version of below updates done in Alphas.

Expand All @@ -58,7 +69,7 @@ Modular SDK - consists stable version of below updates done in Alphas.

### Bug Fixes

VERSION bump only
VERSION Bump Only.



Expand Down
18 changes: 10 additions & 8 deletions packages/account/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@biconomy/account",
"version": "3.1.1",
"version": "3.1.2",
"description": "This package provides apis for ERC-4337 based smart account implementations",
"main": "./dist/src/index.js",
"typings": "./dist/src/index.d.ts",
Expand Down Expand Up @@ -34,18 +34,20 @@
"access": "public"
},
"devDependencies": {
"nock": "^13.2.9"
"nock": "^13.2.9",
"viem": "^1.19.11"
},
"dependencies": {
"@account-abstraction/contracts": "^0.6.0",
"@account-abstraction/utils": "^0.4.0",
"@alchemy/aa-core": "^1.2.2",
"@biconomy-devx/account-contracts-v2": "npm:@biconomy-devx/account-contracts-v2@^1.0.0",
"@biconomy/bundler": "^3.1.1",
"@biconomy/common": "^3.1.1",
"@biconomy/core-types": "^3.1.1",
"@biconomy/modules": "^3.1.1",
"@biconomy/node-client": "^3.1.1",
"@biconomy/paymaster": "^3.1.1",
"@biconomy/bundler": "^3.1.2",
"@biconomy/common": "^3.1.2",
"@biconomy/core-types": "^3.1.2",
"@biconomy/modules": "^3.1.2",
"@biconomy/node-client": "^3.1.2",
"@biconomy/paymaster": "^3.1.2",
"@ethersproject/logger": "^5.7.0",
"@ethersproject/providers": "^5.7.2",
"ethers": "^5.7.0",
Expand Down
33 changes: 26 additions & 7 deletions packages/account/src/BaseSmartAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { IBaseSmartAccount } from "./interfaces/IBaseSmartAccount";
import { defaultAbiCoder, keccak256 } from "ethers/lib/utils";
import { UserOperation, ChainId } from "@biconomy/core-types";
import { calcPreVerificationGas, DefaultGasLimits } from "./utils/Preverificaiton";
import { NotPromise, packUserOp, Logger, RPC_PROVIDER_URLS } from "@biconomy/common";
import { IBundler, UserOpResponse } from "@biconomy/bundler";
import { NotPromise, packUserOp, Logger, RPC_PROVIDER_URLS, isNullOrUndefined } from "@biconomy/common";
import { Bundler, IBundler, UserOpResponse } from "@biconomy/bundler";
import { IPaymaster, PaymasterAndDataResponse } from "@biconomy/paymaster";
import { SendUserOpParams } from "@biconomy/modules";
import { SponsorUserOperationDto, BiconomyPaymaster, PaymasterMode, IHybridPaymaster } from "@biconomy/paymaster";
Expand Down Expand Up @@ -47,10 +47,22 @@ export abstract class BaseSmartAccount implements IBaseSmartAccount {
this.overheads = _smartAccountConfig.overheads;
this.entryPointAddress = _smartAccountConfig.entryPointAddress ?? DEFAULT_ENTRYPOINT_ADDRESS;
this.accountAddress = _smartAccountConfig.accountAddress;
this.paymaster = _smartAccountConfig.paymaster;
this.bundler = _smartAccountConfig.bundler;

this.chainId = _smartAccountConfig.chainId;

if (_smartAccountConfig.bundlerUrl) {
this.bundler = new Bundler({
bundlerUrl: _smartAccountConfig.bundlerUrl,
chainId: _smartAccountConfig.chainId,
});
} else {
this.bundler = _smartAccountConfig.bundler;
}

if (_smartAccountConfig.paymaster) {
this.paymaster = _smartAccountConfig.paymaster;
}

this.provider = _smartAccountConfig.provider ?? new JsonRpcProvider(RPC_PROVIDER_URLS[this.chainId]);

// Create an instance of the EntryPoint contract using the provided address and provider (facory "connect" contract address)
Expand All @@ -72,7 +84,7 @@ export abstract class BaseSmartAccount implements IBaseSmartAccount {

validateUserOp(userOp: Partial<UserOperation>, requiredFields: UserOperationKey[]): boolean {
for (const field of requiredFields) {
if (!userOp[field]) {
if (isNullOrUndefined(userOp[field])) {
throw new Error(`${String(field)} is missing in the UserOp`);
}
}
Expand Down Expand Up @@ -254,14 +266,17 @@ export abstract class BaseSmartAccount implements IBaseSmartAccount {

if (skipBundlerCall) {
if (this.paymaster && this.paymaster instanceof BiconomyPaymaster) {
if (!userOp.maxFeePerGas && !userOp.maxPriorityFeePerGas) {
if (isNullOrUndefined(userOp.maxFeePerGas) || isNullOrUndefined(userOp.maxPriorityFeePerGas)) {
throw new Error("maxFeePerGas and maxPriorityFeePerGas are required for skipBundlerCall mode");
}
if (paymasterServiceData?.mode === PaymasterMode.SPONSORED) {
// Making call to paymaster to get gas estimations for userOp
const { callGasLimit, verificationGasLimit, preVerificationGas, paymasterAndData } = await (
this.paymaster as IHybridPaymaster<SponsorUserOperationDto>
).getPaymasterAndData(userOp, paymasterServiceData);
if (paymasterAndData === "0x" && (callGasLimit === undefined || verificationGasLimit === undefined || preVerificationGas === undefined)) {
throw new Error("Since you intend to use sponsorship paymaster, please check and make sure policies are set on the dashboard");
}
finalUserOp.verificationGasLimit = verificationGasLimit ?? userOp.verificationGasLimit;
finalUserOp.callGasLimit = callGasLimit ?? userOp.callGasLimit;
finalUserOp.preVerificationGas = preVerificationGas ?? userOp.preVerificationGas;
Expand All @@ -287,7 +302,11 @@ export abstract class BaseSmartAccount implements IBaseSmartAccount {
const { callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas, maxPriorityFeePerGas } =
await this.bundler.estimateUserOpGas(userOp);
// if neither user sent gas fee nor the bundler, estimate gas from provider
if (!userOp.maxFeePerGas && !userOp.maxPriorityFeePerGas && (!maxFeePerGas || !maxPriorityFeePerGas)) {
if (
isNullOrUndefined(userOp.maxFeePerGas) &&
isNullOrUndefined(userOp.maxPriorityFeePerGas) &&
(isNullOrUndefined(maxFeePerGas) || isNullOrUndefined(maxPriorityFeePerGas))
) {
const feeData = await this.provider.getFeeData();
finalUserOp.maxFeePerGas = feeData.maxFeePerGas ?? feeData.gasPrice ?? (await this.provider.getGasPrice());
finalUserOp.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? feeData.gasPrice ?? (await this.provider.getGasPrice());
Expand Down
7 changes: 4 additions & 3 deletions packages/account/src/BiconomySmartAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
getEntryPointContract,
getSAFactoryContract,
getSAProxyContract,
isNullOrUndefined,
} from "@biconomy/common";
import { BiconomySmartAccountConfig, Overrides, BiconomyTokenPaymasterRequest, InitilizationData } from "./utils/Types";
import { UserOperation, Transaction, SmartAccountType } from "@biconomy/core-types";
Expand Down Expand Up @@ -343,7 +344,7 @@ export class BiconomySmartAccount extends SmartAccount implements IBiconomySmart
}

private validateUserOpAndRequest(userOp: Partial<UserOperation>, tokenPaymasterRequest: BiconomyTokenPaymasterRequest): void {
if (!userOp.callData) {
if (isNullOrUndefined(userOp.callData)) {
throw new Error("Userop callData cannot be undefined");
}

Expand Down Expand Up @@ -398,12 +399,12 @@ export class BiconomySmartAccount extends SmartAccount implements IBiconomySmart
return userOp;
}

if (!userOp.callData) {
if (isNullOrUndefined(userOp.callData)) {
throw new Error("Userop callData cannot be undefined");
}

const decodedDataSmartWallet = this.proxy.interface.parseTransaction({
data: userOp.callData.toString(),
data: userOp.callData!.toString(),
});
if (!decodedDataSmartWallet) {
throw new Error("Could not parse call data of smart wallet for userOp");
Expand Down
37 changes: 31 additions & 6 deletions packages/account/src/BiconomySmartAccountV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
SmartAccountFactory_v200__factory,
AddressResolver,
AddressResolver__factory,
isNullOrUndefined,
} from "@biconomy/common";
import {
BiconomyTokenPaymasterRequest,
Expand All @@ -22,7 +23,7 @@ import {
SmartAccountInfo,
QueryParamsForAddressResolver,
} from "./utils/Types";
import { BaseValidationModule, ModuleInfo, SendUserOpParams } from "@biconomy/modules";
import { BaseValidationModule, ECDSAOwnershipValidationModule, 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 Down Expand Up @@ -80,10 +81,27 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount {
super(biconomySmartAccountConfig);
}

/**
* Creates a new instance of BiconomySmartAccountV2.
*
* This method will create a BiconomySmartAccountV2 instance but will not deploy the Smart Account.
*
* Deployment of the Smart Account will be donewith the first user operation.
*
* @param biconomySmartAccountConfig - Configuration for initializing the BiconomySmartAccountV2 instance.
* @returns A promise that resolves to a new instance of BiconomySmartAccountV2.
* @throws An error if something is wrong with the smart account instance creation.
*/
public static async create(biconomySmartAccountConfig: BiconomySmartAccountV2Config): Promise<BiconomySmartAccountV2> {
const instance = new BiconomySmartAccountV2(biconomySmartAccountConfig);
instance.factoryAddress = biconomySmartAccountConfig.factoryAddress ?? DEFAULT_BICONOMY_FACTORY_ADDRESS; // This would be fetched from V2

if (biconomySmartAccountConfig.biconomyPaymasterApiKey) {
instance.paymaster = new BiconomyPaymaster({
paymasterUrl: `https://paymaster.biconomy.io/api/v1/${biconomySmartAccountConfig.chainId}/${biconomySmartAccountConfig.biconomyPaymasterApiKey}`,
});
}

const defaultFallbackHandlerAddress =
instance.factoryAddress === DEFAULT_BICONOMY_FACTORY_ADDRESS
? DEFAULT_FALLBACK_HANDLER_ADDRESS
Expand All @@ -96,8 +114,15 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount {

instance.implementationAddress = biconomySmartAccountConfig.implementationAddress ?? BICONOMY_IMPLEMENTATION_ADDRESSES_BY_VERSION.V2_0_0;

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
instance.defaultValidationModule = biconomySmartAccountConfig.defaultValidationModule;
// Note: if no module is provided, we will use ECDSA_OWNERSHIP as default
if (biconomySmartAccountConfig.defaultValidationModule) {
instance.defaultValidationModule = biconomySmartAccountConfig.defaultValidationModule;
} else {
instance.defaultValidationModule = await ECDSAOwnershipValidationModule.create({
signer: biconomySmartAccountConfig.signer!,
});
}

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
instance.activeValidationModule = biconomySmartAccountConfig.activeValidationModule ?? instance.defaultValidationModule;

Expand Down Expand Up @@ -480,7 +505,7 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount {
}

private validateUserOpAndPaymasterRequest(userOp: Partial<UserOperation>, tokenPaymasterRequest: BiconomyTokenPaymasterRequest): void {
if (!userOp.callData) {
if (isNullOrUndefined(userOp.callData)) {
throw new Error("UserOp callData cannot be undefined");
}

Expand Down Expand Up @@ -535,14 +560,14 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount {
return userOp;
}

if (!userOp.callData) {
if (isNullOrUndefined(userOp.callData)) {
throw new Error("UserOp callData cannot be undefined");
}

const account = await this._getAccountContract();

const decodedSmartAccountData = account.interface.parseTransaction({
data: userOp.callData.toString(),
data: userOp.callData!.toString(),
});
if (!decodedSmartAccountData) {
throw new Error("Could not parse userOp call data for this smart account");
Expand Down
15 changes: 11 additions & 4 deletions packages/account/src/SmartAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ISmartAccount } from "./interfaces/ISmartAccount";
import { defaultAbiCoder, keccak256, arrayify } from "ethers/lib/utils";
import { UserOperation, ChainId } from "@biconomy/core-types";
import { calcPreVerificationGas, DefaultGasLimits } from "./utils/Preverificaiton";
import { packUserOp } from "@biconomy/common";
import { packUserOp, isNullOrUndefined } from "@biconomy/common";

import { IBundler, UserOpResponse } from "@biconomy/bundler";
import { IPaymaster, PaymasterAndDataResponse } from "@biconomy/paymaster";
Expand Down Expand Up @@ -49,7 +49,7 @@ export abstract class SmartAccount implements ISmartAccount {

private validateUserOp(userOp: Partial<UserOperation>, requiredFields: UserOperationKey[]): boolean {
for (const field of requiredFields) {
if (!userOp[field]) {
if (isNullOrUndefined(userOp[field])) {
throw new Error(`${String(field)} is missing in the UserOp`);
}
}
Expand Down Expand Up @@ -117,7 +117,7 @@ export abstract class SmartAccount implements ISmartAccount {

if (skipBundlerCall) {
if (this.paymaster && this.paymaster instanceof BiconomyPaymaster) {
if (!userOp.maxFeePerGas && !userOp.maxPriorityFeePerGas) {
if (isNullOrUndefined(userOp.maxFeePerGas) || isNullOrUndefined(userOp.maxPriorityFeePerGas)) {
throw new Error("maxFeePerGas and maxPriorityFeePerGas are required for skipBundlerCall mode");
}
if (paymasterServiceData?.mode === PaymasterMode.SPONSORED) {
Expand All @@ -132,6 +132,9 @@ export abstract class SmartAccount implements ISmartAccount {
const { callGasLimit, verificationGasLimit, preVerificationGas, paymasterAndData } = await (
this.paymaster as IHybridPaymaster<SponsorUserOperationDto>
).getPaymasterAndData(userOp, paymasterServiceData);
if (paymasterAndData === "0x" && (callGasLimit === undefined || verificationGasLimit === undefined || preVerificationGas === undefined)) {
throw new Error("Since you intend to use sponsorship paymaster, please check and make sure policies are set on the dashboard");
}
finalUserOp.verificationGasLimit = verificationGasLimit ?? userOp.verificationGasLimit;
finalUserOp.callGasLimit = callGasLimit ?? userOp.callGasLimit;
finalUserOp.preVerificationGas = preVerificationGas ?? userOp.preVerificationGas;
Expand All @@ -155,7 +158,11 @@ export abstract class SmartAccount implements ISmartAccount {
const { callGasLimit, verificationGasLimit, preVerificationGas, maxFeePerGas, maxPriorityFeePerGas } =
await this.bundler.estimateUserOpGas(userOp);
// if neither user sent gas fee nor the bundler, estimate gas from provider
if (!userOp.maxFeePerGas && !userOp.maxPriorityFeePerGas && (!maxFeePerGas || !maxPriorityFeePerGas)) {
if (
isNullOrUndefined(userOp.maxFeePerGas) &&
isNullOrUndefined(userOp.maxPriorityFeePerGas) &&
(isNullOrUndefined(maxFeePerGas) || isNullOrUndefined(maxPriorityFeePerGas))
) {
const feeData = await this.provider.getFeeData();
finalUserOp.maxFeePerGas = feeData.maxFeePerGas ?? feeData.gasPrice ?? (await this.provider.getGasPrice());
finalUserOp.maxPriorityFeePerGas = feeData.maxPriorityFeePerGas ?? feeData.gasPrice ?? (await this.provider.getGasPrice());
Expand Down
Loading

0 comments on commit 7a165b0

Please sign in to comment.