diff --git a/linkAll.sh b/linkAll.sh new file mode 100755 index 000000000..6e9f511b6 --- /dev/null +++ b/linkAll.sh @@ -0,0 +1,2 @@ +!/bin/sh +for dir in ./packages/*; do (cd "$dir" && yarn link); done \ No newline at end of file diff --git a/package.json b/package.json index 35f668de6..65f030a37 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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", @@ -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", diff --git a/packages/account/CHANGELOG.md b/packages/account/CHANGELOG.md index f85934702..5d86ed497 100644 --- a/packages/account/CHANGELOG.md +++ b/packages/account/CHANGELOG.md @@ -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) @@ -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. @@ -58,7 +69,7 @@ Modular SDK - consists stable version of below updates done in Alphas. ### Bug Fixes -VERSION bump only +VERSION Bump Only. diff --git a/packages/account/package.json b/packages/account/package.json index 7002d2e3a..68ba8dfcf 100644 --- a/packages/account/package.json +++ b/packages/account/package.json @@ -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", @@ -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", diff --git a/packages/account/src/BaseSmartAccount.ts b/packages/account/src/BaseSmartAccount.ts index 36b997526..90bb0ecff 100644 --- a/packages/account/src/BaseSmartAccount.ts +++ b/packages/account/src/BaseSmartAccount.ts @@ -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"; @@ -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) @@ -72,7 +84,7 @@ export abstract class BaseSmartAccount implements IBaseSmartAccount { validateUserOp(userOp: Partial, 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`); } } @@ -254,7 +266,7 @@ 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) { @@ -262,6 +274,9 @@ export abstract class BaseSmartAccount implements IBaseSmartAccount { const { callGasLimit, verificationGasLimit, preVerificationGas, paymasterAndData } = await ( this.paymaster as IHybridPaymaster ).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; @@ -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()); diff --git a/packages/account/src/BiconomySmartAccount.ts b/packages/account/src/BiconomySmartAccount.ts index e5dba89bf..3307856b7 100644 --- a/packages/account/src/BiconomySmartAccount.ts +++ b/packages/account/src/BiconomySmartAccount.ts @@ -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"; @@ -343,7 +344,7 @@ export class BiconomySmartAccount extends SmartAccount implements IBiconomySmart } private validateUserOpAndRequest(userOp: Partial, tokenPaymasterRequest: BiconomyTokenPaymasterRequest): void { - if (!userOp.callData) { + if (isNullOrUndefined(userOp.callData)) { throw new Error("Userop callData cannot be undefined"); } @@ -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"); diff --git a/packages/account/src/BiconomySmartAccountV2.ts b/packages/account/src/BiconomySmartAccountV2.ts index aec1a11fb..fe9ce9e39 100644 --- a/packages/account/src/BiconomySmartAccountV2.ts +++ b/packages/account/src/BiconomySmartAccountV2.ts @@ -11,6 +11,7 @@ import { SmartAccountFactory_v200__factory, AddressResolver, AddressResolver__factory, + isNullOrUndefined, } from "@biconomy/common"; import { BiconomyTokenPaymasterRequest, @@ -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"; @@ -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 { 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 @@ -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; @@ -480,7 +505,7 @@ export class BiconomySmartAccountV2 extends BaseSmartAccount { } private validateUserOpAndPaymasterRequest(userOp: Partial, tokenPaymasterRequest: BiconomyTokenPaymasterRequest): void { - if (!userOp.callData) { + if (isNullOrUndefined(userOp.callData)) { throw new Error("UserOp callData cannot be undefined"); } @@ -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"); diff --git a/packages/account/src/SmartAccount.ts b/packages/account/src/SmartAccount.ts index 66efbbeae..7ca709e97 100644 --- a/packages/account/src/SmartAccount.ts +++ b/packages/account/src/SmartAccount.ts @@ -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"; @@ -49,7 +49,7 @@ export abstract class SmartAccount implements ISmartAccount { private validateUserOp(userOp: Partial, 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`); } } @@ -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) { @@ -132,6 +132,9 @@ export abstract class SmartAccount implements ISmartAccount { const { callGasLimit, verificationGasLimit, preVerificationGas, paymasterAndData } = await ( this.paymaster as IHybridPaymaster ).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; @@ -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()); diff --git a/packages/account/src/utils/Types.ts b/packages/account/src/utils/Types.ts index b3a2cf448..942166b5b 100644 --- a/packages/account/src/utils/Types.ts +++ b/packages/account/src/utils/Types.ts @@ -6,6 +6,7 @@ import { BaseValidationModule, ModuleInfo } from "@biconomy/modules"; import { Provider } from "@ethersproject/providers"; import { GasOverheads } from "./Preverificaiton"; import { UserOperation, ChainId } from "@biconomy/core-types"; +import { WalletClientSigner } from "@alchemy/aa-core"; export type EntryPointAddresses = { [address: string]: string; @@ -36,17 +37,28 @@ export type SmartAccountConfig = { bundler?: IBundler; }; -export interface BaseSmartAccountConfig { +/** + * Enum representing available validation modules. + * + * - `ECDSA_OWNERSHIP`: Default module for ECDSA ownership validation. + * - `MULTICHAIN`: Default module for multi-chain validation. + * - If you don't provide any module, ECDSA_OWNERSHIP will be used as default + */ +/*export enum AuthorizationModuleType { + ECDSA_OWNERSHIP = DEFAULT_ECDSA_OWNERSHIP_MODULE, + // MULTICHAIN = DEFAULT_MULTICHAIN_MODULE, +}*/ + +export type BaseSmartAccountConfig = ConditionalBundlerProps & { // owner?: Signer // can be in child classes index?: number; provider?: Provider; - entryPointAddress: string; + entryPointAddress?: string; accountAddress?: string; overheads?: Partial; paymaster?: IPaymaster; // PaymasterAPI - bundler?: IBundler; // like HttpRpcClient chainId: ChainId; -} +}; export type BiconomyTokenPaymasterRequest = { feeQuote: PaymasterFeeQuote; @@ -64,18 +76,40 @@ export type BiconomySmartAccountConfig = { nodeClientUrl?: string; }; -export interface BiconomySmartAccountV2Config extends BaseSmartAccountConfig { - factoryAddress?: string; - senderAddress?: string; - implementationAddress?: string; - defaultFallbackHandler?: string; - rpcUrl?: string; // as good as Provider - nodeClientUrl?: string; // very specific to Biconomy - defaultValidationModule: BaseValidationModule; - activeValidationModule?: BaseValidationModule; - scanForUpgradedAccountsFromV1?: boolean; - maxIndexForScan?: number; -} +type RequireAtLeastOne = Pick> & + { + [K in Keys]-?: Required> & Partial>>; + }[Keys]; + +type ConditionalValidationProps = RequireAtLeastOne< + { + defaultValidationModule: BaseValidationModule; + signer: Signer | WalletClientSigner; + }, + "defaultValidationModule" | "signer" +>; + +type ConditionalBundlerProps = RequireAtLeastOne< + { + bundler: IBundler; + bundlerUrl: string; + }, + "bundler" | "bundlerUrl" +>; + +export type BiconomySmartAccountV2Config = BaseSmartAccountConfig & + ConditionalValidationProps & { + factoryAddress?: string; + senderAddress?: string; + implementationAddress?: string; + defaultFallbackHandler?: string; + biconomyPaymasterApiKey?: string; + rpcUrl?: string; + nodeClientUrl?: string; + activeValidationModule?: BaseValidationModule; + scanForUpgradedAccountsFromV1?: boolean; + maxIndexForScan?: number; + }; export type BuildUserOpOptions = { overrides?: Overrides; diff --git a/packages/account/tests/SmartAccountV2-Abstract-Paymaster.local.spec.ts b/packages/account/tests/SmartAccountV2-Abstract-Paymaster.local.spec.ts new file mode 100644 index 000000000..f4c847b00 --- /dev/null +++ b/packages/account/tests/SmartAccountV2-Abstract-Paymaster.local.spec.ts @@ -0,0 +1,106 @@ +import { EntryPoint, EntryPoint__factory } from "@account-abstraction/contracts"; +import { Wallet, ethers } from "ethers"; + +import { + SmartAccount_v200, + SmartAccountFactory_v200, + SmartAccount_v200__factory, + SmartAccountFactory_v200__factory, + ECDSAOwnershipRegistryModule_v100__factory, +} from "@biconomy/common"; + +import { BiconomySmartAccountV2 } from "../src/BiconomySmartAccountV2"; +import { ChainId } from "@biconomy/core-types"; +import { ECDSAOwnershipValidationModule } from "@biconomy/modules"; +import { BaseValidationModule } from "@biconomy/modules"; +import { ECDSAOwnershipRegistryModule_v100 } from "@biconomy/common"; +import { BiconomyPaymaster } from "@biconomy/paymaster"; + +const provider = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545"); +const signer = provider.getSigner(); + +describe("BiconomySmartAccountV2 Paymaster Abstraction", () => { + let owner: Wallet; + let factoryOwner: Wallet; + let account: BiconomySmartAccountV2; + let entryPoint: EntryPoint; + let accountFactory: SmartAccountFactory_v200; + let ecdsaModule: ECDSAOwnershipRegistryModule_v100; + + let module1: BaseValidationModule; + + beforeAll(async () => { + owner = Wallet.createRandom(); + entryPoint = await new EntryPoint__factory(signer).deploy(); + console.log("ep address ", entryPoint.address); + factoryOwner = Wallet.createRandom(); + + const accountImpl: SmartAccount_v200 = await new SmartAccount_v200__factory(signer).deploy(entryPoint.address); + + accountFactory = await new SmartAccountFactory_v200__factory(signer).deploy(accountImpl.address, await factoryOwner.getAddress()); + + ecdsaModule = await new ECDSAOwnershipRegistryModule_v100__factory(signer).deploy(); + + module1 = await ECDSAOwnershipValidationModule.create({ + signer: owner, + moduleAddress: ecdsaModule.address, + }); + + console.log("provider url ", provider.connection.url); + + await new Promise((resolve) => setTimeout(resolve, 10000)); + }, 30000); + + it("Create a smart account with paymaster through api key", async () => { + + account = await BiconomySmartAccountV2.create({ + chainId: ChainId.GANACHE, + rpcUrl: "http://127.0.0.1:8545", + entryPointAddress: entryPoint.address, + biconomyPaymasterApiKey: "7K_k68BFN.ed274da8-69a1-496d-a897-508fc2213216", + factoryAddress: accountFactory.address, + defaultFallbackHandler: await accountFactory.minimalHandler(), + defaultValidationModule: module1, + activeValidationModule: module1, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." + }); + + const address = await account.getAccountAddress(); + console.log("account address ", address); + + const paymaster = account.paymaster; + + expect(paymaster).not.toBeNull() + expect(paymaster).not.toBeUndefined() + + expect(address).toBe(account.accountAddress); + }, 10000); + + it("Create a smart account with paymaster by creating instance", async () => { + + const paymaster = new BiconomyPaymaster({ + paymasterUrl: "https://paymaster.biconomy.io/api/v1/80001/7K_k68BFN.ed274da8-69a1-496d-a897-508fc2213216", + }) + + account = await BiconomySmartAccountV2.create({ + chainId: ChainId.GANACHE, + rpcUrl: "http://127.0.0.1:8545", + entryPointAddress: entryPoint.address, + factoryAddress: accountFactory.address, + defaultFallbackHandler: await accountFactory.minimalHandler(), + defaultValidationModule: module1, + activeValidationModule: module1, + paymaster: paymaster, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." + }); + + const address = await account.getAccountAddress(); + console.log("account address ", address); + + expect(account.paymaster).not.toBeNull() + expect(account.paymaster).not.toBeUndefined() + + expect(address).toBe(account.accountAddress); + }, 10000); + +}); \ No newline at end of file diff --git a/packages/account/tests/SmartAccountV2-Module-Abstraction.local.spec.ts b/packages/account/tests/SmartAccountV2-Module-Abstraction.local.spec.ts new file mode 100644 index 000000000..53d468752 --- /dev/null +++ b/packages/account/tests/SmartAccountV2-Module-Abstraction.local.spec.ts @@ -0,0 +1,87 @@ +import { EntryPoint, EntryPoint__factory } from "@account-abstraction/contracts"; +import { Wallet, ethers } from "ethers"; +import { SampleRecipient, SampleRecipient__factory } from "@account-abstraction/utils/dist/src/types"; + +import { + SmartAccount_v200, + SmartAccountFactory_v200, + SmartAccount_v200__factory, + SmartAccountFactory_v200__factory, + ECDSAOwnershipRegistryModule_v100__factory, +} from "@biconomy/common"; + +import { BiconomySmartAccountV2 } from "../src/BiconomySmartAccountV2"; +import { ChainId } from "@biconomy/core-types"; +import { ECDSAOwnershipRegistryModule_v100 } from "@biconomy/common"; +import { ECDSAOwnershipValidationModule } from "@biconomy/modules"; + +const provider = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545"); +const signer = provider.getSigner(); + +describe("BiconomySmartAccountV2 Module Abstraction", () => { + let owner: Wallet; + let factoryOwner: Wallet; + let entryPoint: EntryPoint; + let beneficiary: string; + let recipient: SampleRecipient; + let accountFactory: SmartAccountFactory_v200; + let ecdsaModule: ECDSAOwnershipRegistryModule_v100; + + beforeAll(async () => { + owner = Wallet.createRandom(); + entryPoint = await new EntryPoint__factory(signer).deploy(); + console.log("ep address ", entryPoint.address); + beneficiary = await signer.getAddress(); + factoryOwner = Wallet.createRandom(); + + const accountImpl: SmartAccount_v200 = await new SmartAccount_v200__factory(signer).deploy(entryPoint.address); + + accountFactory = await new SmartAccountFactory_v200__factory(signer).deploy(accountImpl.address, await factoryOwner.getAddress()); + + ecdsaModule = await new ECDSAOwnershipRegistryModule_v100__factory(signer).deploy(); + + recipient = await new SampleRecipient__factory(signer).deploy(); + + await new Promise((resolve) => setTimeout(resolve, 10000)); + }, 30000); + + it("Create smart account with default module (ECDSA)", async () => { + const account: BiconomySmartAccountV2 = await BiconomySmartAccountV2.create({ + chainId: ChainId.GANACHE, + rpcUrl: "http://127.0.0.1:8545", + entryPointAddress: entryPoint.address, + signer, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." + /*defaultValidationModule: await ECDSAOwnershipValidationModule.create({ + signer: signer, + moduleAddress: ecdsaModule.address, + }),*/ + }); + + const address = await account.getAccountAddress(); + console.log("Module Abstraction Test - Account address ", address); + + expect(address).toBe(account.accountAddress); + + const module = account.activeValidationModule; + console.log(`ACTIVE MODULE - ${module.getAddress()}`); + }, 10000); + + it("Create smart account with ECDSA module", async () => { + const account: BiconomySmartAccountV2 = await BiconomySmartAccountV2.create({ + chainId: ChainId.GANACHE, + rpcUrl: "http://127.0.0.1:8545", + entryPointAddress: entryPoint.address, + signer, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." + }); + + const address = await account.getAccountAddress(); + console.log("Module Abstraction Test - Account address ", address); + + expect(address).toBe(account.accountAddress); + + const module = account.activeValidationModule; + console.log(`ACTIVE MODULE - ${module.getAddress()}`); + }, 10000); +}); diff --git a/packages/account/tests/SmartAccountV2.local.spec.ts b/packages/account/tests/SmartAccountV2.local.spec.ts index cea9f406c..da8aea562 100644 --- a/packages/account/tests/SmartAccountV2.local.spec.ts +++ b/packages/account/tests/SmartAccountV2.local.spec.ts @@ -1,4 +1,4 @@ -import { EntryPoint, EntryPoint__factory, UserOperationStruct, SimpleAccountFactory__factory } from "@account-abstraction/contracts"; +import { EntryPoint, EntryPoint__factory } from "@account-abstraction/contracts"; import { VoidSigner, Wallet, ethers } from "ethers"; import { SampleRecipient, SampleRecipient__factory } from "@account-abstraction/utils/dist/src/types"; @@ -18,11 +18,20 @@ import { MultiChainValidationModule } from "@biconomy/modules"; import { BaseValidationModule } from "@biconomy/modules"; import { ECDSAOwnershipRegistryModule_v100 } from "@biconomy/common"; import { MultiChainValidationModule_v100 } from "@biconomy/common"; +import { createWalletClient, http } from "viem"; +import { localhost, polygonMumbai } from "viem/chains"; +import { WalletClientSigner } from "@alchemy/aa-core"; +import { privateKeyToAccount } from "viem/accounts"; +import { DEFAULT_ENTRYPOINT_ADDRESS } from "../src/utils/Constants"; const provider = new ethers.providers.JsonRpcProvider("http://127.0.0.1:8545"); const signer = provider.getSigner(); const SENTINEL_MODULE = "0x0000000000000000000000000000000000000001"; +const MUMBAI = "https://rpc-mumbai.maticvigil.com"; +const randomEOA = ethers.Wallet.createRandom(); +const testPrivKey = randomEOA.privateKey.slice(2); + describe("BiconomySmartAccountV2 API Specs", () => { let owner: Wallet; let factoryOwner: Wallet; @@ -77,6 +86,8 @@ describe("BiconomySmartAccountV2 API Specs", () => { defaultFallbackHandler: await accountFactory.minimalHandler(), defaultValidationModule: module1, activeValidationModule: module1, + signer, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." }); // console.log('account api provider ', accountAPI.provider) @@ -91,11 +102,11 @@ describe("BiconomySmartAccountV2 API Specs", () => { const builtUserOp = await accountAPI.buildUserOp([{ to: recipient.address, value: ethers.utils.parseEther("1".toString()), data: "0x" }]); console.log("builtUserOp", builtUserOp); expect(builtUserOp?.nonce?.toString()).toBe("0"); - }); + }, 30000); it("Sender should be non zero", async () => { const builtUserOp = await accountAPI.buildUserOp([{ to: recipient.address, value: ethers.utils.parseEther("1".toString()), data: "0x" }]); expect(builtUserOp.sender).not.toBe(ethers.constants.AddressZero); - }); + }, 30000); it("InitCode length should be greater then 170", async () => { const builtUserOp = await accountAPI.buildUserOp([{ to: recipient.address, value: ethers.utils.parseEther("1".toString()), data: "0x" }]); expect(builtUserOp?.initCode?.length).toBeGreaterThan(170); @@ -158,6 +169,8 @@ describe("BiconomySmartAccountV2 API Specs", () => { defaultFallbackHandler: await accountFactory.minimalHandler(), defaultValidationModule: module2, activeValidationModule: module2, + signer, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." }); // TODO @@ -350,6 +363,8 @@ describe("BiconomySmartAccountV2 API Specs", () => { defaultFallbackHandler: await accountFactory.minimalHandler(), defaultValidationModule: newmodule, activeValidationModule: newmodule, + signer, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." }); const address = await accountAPI2.getAccountAddress(); @@ -358,8 +373,237 @@ describe("BiconomySmartAccountV2 API Specs", () => { expect(address).toBe(accountAPI.accountAddress); }, 10000); + it("Create and setup ECDSA module with WalletClientSigner", async () => { + const wallet = privateKeyToAccount(`0x${testPrivKey}`); + + const walletClient = createWalletClient({ + account: wallet, + chain: polygonMumbai, + transport: http(MUMBAI), + }); + + const ecdsaSigner = new WalletClientSigner(walletClient, "json-rpc"); + + const account = await BiconomySmartAccountV2.create({ + chainId: ChainId.POLYGON_MUMBAI, + entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, + signer: ecdsaSigner, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." + }); + + const counterFactualAddress = await account.getAccountAddress(); + console.log("Counterfactual address ", counterFactualAddress); + + const module = await ECDSAOwnershipValidationModule.create({ + signer: owner, + moduleAddress: DEFAULT_ECDSA_OWNERSHIP_MODULE, + }); + + account.setActiveValidationModule(module); + }); + + it("Create and setup ECDSA module with ethersV5 Signer", async () => { + const module = await ECDSAOwnershipValidationModule.create({ + signer: randomEOA, + moduleAddress: DEFAULT_ECDSA_OWNERSHIP_MODULE, + }); + + const account = await BiconomySmartAccountV2.create({ + chainId: ChainId.POLYGON_MUMBAI, + entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, + signer: owner, + defaultValidationModule: module, + activeValidationModule: module, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." + }); + + const counterFactualAddress = await account.getAccountAddress(); + console.log("Counterfactual address ", counterFactualAddress); + + expect(counterFactualAddress).toBeDefined(); + + expect(module.getAddress()).toBe(DEFAULT_ECDSA_OWNERSHIP_MODULE); + }); + + // NOTE + // For tests we could only use sendUserOp for test networks until bundler integration test suite is integrated + // For test networks we can send transactions for Account created using random private key, IF paymaster is used + // buildUserOp tests we can do for any test network cause that only requires bundles without sending transactions + // If we can send prefund to the account then specific private key can be added (only testnet native tokens) or loaded from env // TODO - // 1. sendSignedUserOp() - // 2. sendUserOp() - // 3. sending userOps using a paymaster + + // it("Send user op with ethersV5 signer", async () => { + + // const provider = new ethers.providers.JsonRpcProvider(MUMBAI); + // const owner: Signer = new ethers.Wallet(testPrivKey, provider); + + // const bundler: IBundler = new Bundler({ + // bundlerUrl: "", + // chainId: ChainId.POLYGON_MUMBAI, + // entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, + // }) + + // const module = await ECDSAOwnershipValidationModule.create({ + // signer: owner, + // moduleAddress: DEFAULT_ECDSA_OWNERSHIP_MODULE + // }) + + // const newAccount = await BiconomySmartAccountV2.create({ + // chainId: ChainId.POLYGON_MUMBAI, + // entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, + // signer: owner, + // bundler, + // defaultValidationModule: module, + // activeValidationModule: module + // }); + + // const accountAddress = await newAccount.getAccountAddress(); + + // const prefund = { + // to: accountAddress, + // value: ethers.utils.parseEther("0.1"), + // } + + // const prefundResp = await owner.sendTransaction(prefund); + // prefundResp.wait(); + + // const tx = { + // to: await Wallet.createRandom().getAddress(), + // data: "0x" + // } + + // const userOp = await newAccount.buildUserOp([tx]); + // const res = await newAccount.sendUserOp(userOp); + // const txhash = await res.waitForTxHash(); + + // console.log("txhash ", txhash); + + // expect(txhash).toBeDefined(); + + // }); + + // it("Send user op with WalletClientSigner signer", async () => { + + // const wallet = privateKeyToAccount(`0x${testPrivKey}`) + + // const walletClient = createWalletClient({ + // account: wallet, + // transport: http("https://rpc-mumbai.maticvigil.com"), + // }); + + // let owner = new WalletClientSigner( + // walletClient, + // "json-rpc" + // ); + + // const bundler: IBundler = new Bundler({ + // bundlerUrl: "", + // chainId: ChainId.POLYGON_MUMBAI, + // entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, + // }) + + // const module = await ECDSAOwnershipValidationModule.create({ + // signer: owner, + // moduleAddress: DEFAULT_ECDSA_OWNERSHIP_MODULE + // }) + + // const newAccount = await BiconomySmartAccountV2.create({ + // chainId: ChainId.POLYGON_MUMBAI, + // entryPointAddress: DEFAULT_ENTRYPOINT_ADDRESS, + // signer: owner, + // bundler, + // defaultValidationModule: module, + // activeValidationModule: module + // }); + + // const accountAddress: `0x${string}` = await newAccount.getAccountAddress() as `0x${string}`; + + // const prefundResp = await walletClient.sendTransaction({ + // account: wallet, + // to: accountAddress, + // value: 100000000000000000n, + // chain: polygonMumbai + // }); + + // const tx = { + // to: await Wallet.createRandom().getAddress(), + // data: "0x" + // } + + // const userOp = await newAccount.buildUserOp([tx]); + // const res = await newAccount.sendUserOp(userOp); + // const txhash = await res.waitForTxHash(); + + // console.log("txhash ", txhash); + + // expect(txhash).toBeDefined(); + // }); + + it("Create smart account with default module (ECDSA) without creating instance or providing module name", async () => { + const account: BiconomySmartAccountV2 = await BiconomySmartAccountV2.create({ + chainId: ChainId.GANACHE, + rpcUrl: "http://127.0.0.1:8545", + entryPointAddress: entryPoint.address, + signer, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." + }); + + const address = await account.getAccountAddress(); + console.log("Module Abstraction Test - Account address ", address); + + expect(address).toBe(account.accountAddress); + + const module = account.activeValidationModule; + console.log(`ACTIVE MODULE - ${module.getAddress()}`); + + expect(module.getAddress()).toBe(DEFAULT_ECDSA_OWNERSHIP_MODULE); + }, 10000); + + it("Create smart account with ECDSA module without creating instance", async () => { + const account: BiconomySmartAccountV2 = await BiconomySmartAccountV2.create({ + chainId: ChainId.GANACHE, + rpcUrl: "http://127.0.0.1:8545", + entryPointAddress: entryPoint.address, + signer, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." + }); + + const address = await account.getAccountAddress(); + console.log("Module Abstraction Test - Account address ", address); + + expect(address).toBe(account.accountAddress); + + const module = account.activeValidationModule as ECDSAOwnershipValidationModule; + + console.log(`ACTIVE MODULE - ${module.getAddress()}`); + + expect(module.getAddress()).toBe(DEFAULT_ECDSA_OWNERSHIP_MODULE); + }, 10000); + + it("Create smart account with default module using WalletClientSigner as signer", async () => { + const walletClient = createWalletClient({ + chain: localhost, + transport: http("http://127.0.0.1:8545"), + }); + + const ecdsaSigner = new WalletClientSigner(walletClient, "json-rpc"); + + const account: BiconomySmartAccountV2 = await BiconomySmartAccountV2.create({ + chainId: ChainId.GANACHE, + rpcUrl: "http://127.0.0.1:8545", + entryPointAddress: entryPoint.address, + signer: ecdsaSigner, + bundlerUrl: "https://bundler.biconomy.io/api/v2/1337/..." + }); + + const address = await account.getAccountAddress(); + console.log("Module Abstraction Test - Account address ", address); + + expect(address).toBe(account.accountAddress); + + const module = account.activeValidationModule; + console.log(`ACTIVE MODULE - ${module.getAddress()}`); + + expect(module.getAddress()).toBe(DEFAULT_ECDSA_OWNERSHIP_MODULE); + }, 10000); }); diff --git a/packages/bundler/CHANGELOG.md b/packages/bundler/CHANGELOG.md index 8540b690b..731675d74 100644 --- a/packages/bundler/CHANGELOG.md +++ b/packages/bundler/CHANGELOG.md @@ -3,6 +3,16 @@ 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 entrypoint address optional in bundler config ([547724a](https://github.com/bcnmy/biconomy-client-sdk/pull/337/commits/547724a15366ee1e63aee80fdee0edc128a84c41)) + +### 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)) + ## 3.1.1 (2023-11-09) @@ -56,7 +66,7 @@ Modular SDK - consists stable version of below updates done in Alphas. ## 3.0.0-alpha.0 (2023-08-02) -VERSION bump only +VERSION Bump Only. diff --git a/packages/bundler/package.json b/packages/bundler/package.json index a0a96dcff..15069a0a8 100644 --- a/packages/bundler/package.json +++ b/packages/bundler/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/bundler", - "version": "3.1.1", + "version": "3.1.2", "description": "Biconomy Bundler package to interact with any bundler node as per ERC4337 standard", "main": "./dist/src/index.js", "typings": "./dist/src/index.d.ts", @@ -37,8 +37,8 @@ "access": "public" }, "dependencies": { - "@biconomy/common": "^3.1.1", - "@biconomy/core-types": "^3.1.1", + "@biconomy/common": "^3.1.2", + "@biconomy/core-types": "^3.1.2", "@ethersproject/providers": "^5.7.2", "ethers": "^5.7.0" } diff --git a/packages/bundler/src/Bundler.ts b/packages/bundler/src/Bundler.ts index 3083b15ec..5c78befbe 100644 --- a/packages/bundler/src/Bundler.ts +++ b/packages/bundler/src/Bundler.ts @@ -24,6 +24,7 @@ import { UserOpWaitForTxHashIntervals, UserOpWaitForTxHashMaxDurationIntervals, UserOpReceiptMaxDurationIntervals, + DEFAULT_ENTRYPOINT_ADDRESS, } from "./utils/Constants"; import { JsonRpcProvider } from "@ethersproject/providers"; @@ -62,6 +63,12 @@ export class Bundler implements IBundler { ...UserOpWaitForTxHashMaxDurationIntervals, ...bundlerConfig.userOpWaitForTxHashMaxDurationIntervals, }; + + if (!bundlerConfig.entryPointAddress) { + this.bundlerConfig.entryPointAddress = DEFAULT_ENTRYPOINT_ADDRESS; + } else { + this.bundlerConfig.entryPointAddress = bundlerConfig.entryPointAddress; + } } private getBundlerUrl(): string { @@ -96,7 +103,7 @@ export class Bundler implements IBundler { const userOpGasResponse = response.result; for (const key in userOpGasResponse) { if (key === "maxFeePerGas" || key === "maxPriorityFeePerGas") continue; - if (!userOpGasResponse[key as keyof UserOpGasResponse]) { + if (userOpGasResponse[key as keyof UserOpGasResponse] === undefined || userOpGasResponse[key as keyof UserOpGasResponse] === null) { throw new Error(`Got undefined ${key} from bundler`); } } diff --git a/packages/bundler/src/utils/Constants.ts b/packages/bundler/src/utils/Constants.ts index 9deecacad..be373789a 100644 --- a/packages/bundler/src/utils/Constants.ts +++ b/packages/bundler/src/utils/Constants.ts @@ -125,3 +125,5 @@ export const UserOpWaitForTxHashMaxDurationIntervals: { [key in ChainId]?: numbe [ChainId.CHILIZ_MAINNET]: 20000, [ChainId.CHILIZ_TESTNET]: 20000, }; + +export const DEFAULT_ENTRYPOINT_ADDRESS = "0x5ff137d4b0fdcd49dca30c7cf57e578a026d2789"; diff --git a/packages/bundler/src/utils/Types.ts b/packages/bundler/src/utils/Types.ts index 92763b483..6e93489d9 100644 --- a/packages/bundler/src/utils/Types.ts +++ b/packages/bundler/src/utils/Types.ts @@ -20,7 +20,7 @@ export type UserOpReceipt = { paymaster: string; actualGasCost: BigNumber; actualGasUsed: BigNumber; - success: boolean; + success: "true" | "false"; reason: string; logs: Array; // The logs generated by this UserOperation (not including logs of other UserOperations in the same bundle) receipt: ethers.providers.TransactionReceipt; diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index 2eb65d5c6..03907bc7c 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -3,6 +3,11 @@ 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) + +VERSION Bump Only. + + ## 3.1.1 (2023-11-09) @@ -49,7 +54,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## 3.0.0-alpha.0 (2023-08-02) -VERSION bump only +VERSION Bump Only. diff --git a/packages/common/package.json b/packages/common/package.json index a908815d9..0e49aa11a 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/common", - "version": "3.1.1", + "version": "3.1.2", "description": "common utils to be used for aa transactions", "keywords": [ "utils" @@ -41,8 +41,8 @@ }, "dependencies": { "@account-abstraction/contracts": "^0.6.0", - "@biconomy/core-types": "^3.1.1", - "@biconomy/node-client": "^3.1.1", + "@biconomy/core-types": "^3.1.2", + "@biconomy/node-client": "^3.1.2", "@ethersproject/abi": "^5.7.0", "@ethersproject/bytes": "^5.7.0", "@ethersproject/providers": "^5.7.0", diff --git a/packages/common/src/Utils.ts b/packages/common/src/Utils.ts index b6f5568d2..c97c7bc41 100644 --- a/packages/common/src/Utils.ts +++ b/packages/common/src/Utils.ts @@ -1,3 +1,5 @@ +import { BigNumber, Bytes } from "ethers"; + /** * @description this function will return current timestamp in seconds * @returns Number @@ -5,3 +7,7 @@ export const getTimestampInSeconds = (): number => { return Math.floor(Date.now() / 1000); }; + +export const isNullOrUndefined = (value: string | number | bigint | BigNumber | Bytes | undefined): value is undefined => { + return value === null || value === undefined; +}; diff --git a/packages/core-types/CHANGELOG.md b/packages/core-types/CHANGELOG.md index 5679c227a..f8aa18f1e 100644 --- a/packages/core-types/CHANGELOG.md +++ b/packages/core-types/CHANGELOG.md @@ -3,6 +3,10 @@ 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) + +VERSION Bump Only. + ## 3.1.1 (2023-11-09) @@ -45,7 +49,7 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline # 3.1.0-alpha.0 (2023-07-24) -VERSION bump only +VERSION Bump Only. # 3.1.0-alpha.0 (2023-07-24) diff --git a/packages/core-types/package.json b/packages/core-types/package.json index bb3de3795..57494be26 100644 --- a/packages/core-types/package.json +++ b/packages/core-types/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/core-types", - "version": "3.1.1", + "version": "3.1.2", "description": "Biconomy Client SDK types", "main": "./dist/src/index.js", "typings": "./dist/src/index.d.ts", diff --git a/packages/modules/CHANGELOG.md b/packages/modules/CHANGELOG.md index d46927c84..370078868 100644 --- a/packages/modules/CHANGELOG.md +++ b/packages/modules/CHANGELOG.md @@ -3,6 +3,12 @@ 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) + +### Bug Fixes + +* Update import paths for consistency and fixing build issues ([ec5c3a3](https://github.com/bcnmy/biconomy-client-sdk/pull/332/commits/ec5c3a352e8caab6e94234264f4cd5cb32e5af3f)) + ## 3.1.1 (2023-11-09) diff --git a/packages/modules/package.json b/packages/modules/package.json index 47550c536..87d27f38d 100644 --- a/packages/modules/package.json +++ b/packages/modules/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/modules", - "version": "3.1.1", + "version": "3.1.2", "description": "This package provides different validation modules/plugins for ERC4337 compatible modular account", "main": "./dist/src/index.js", "typings": "./dist/src/index.d.ts", @@ -34,9 +34,10 @@ "access": "public" }, "dependencies": { - "@biconomy/common": "^3.1.1", - "@biconomy/core-types": "^3.1.1", - "@biconomy/node-client": "^3.1.1", + "@alchemy/aa-core": "^1.2.2", + "@biconomy/common": "^3.1.2", + "@biconomy/core-types": "^3.1.2", + "@biconomy/node-client": "^3.1.2", "ethereumjs-util": "^7.1.5", "ethers": "^5.7.2", "merkletreejs": "^0.3.9" diff --git a/packages/modules/src/BaseValidationModule.ts b/packages/modules/src/BaseValidationModule.ts index 1a1e8cbeb..1fc777e70 100644 --- a/packages/modules/src/BaseValidationModule.ts +++ b/packages/modules/src/BaseValidationModule.ts @@ -3,6 +3,7 @@ import { Bytes } from "ethers/lib/utils"; import { BaseValidationModuleConfig, ModuleInfo } from "./utils/Types"; import { DEFAULT_ENTRYPOINT_ADDRESS } from "./utils/Constants"; import { IValidationModule } from "./interfaces/IValidationModule"; +import { WalletClientSigner } from "@alchemy/aa-core"; export abstract class BaseValidationModule implements IValidationModule { entryPointAddress: string; @@ -24,10 +25,34 @@ export abstract class BaseValidationModule implements IValidationModule { // Anything required to get dummy signature can be passed as params abstract getDummySignature(_params?: ModuleInfo): Promise; - abstract getSigner(): Promise; + abstract getSigner(): Promise; // Signer specific or any other additional information can be passed as params abstract signUserOpHash(_userOpHash: string, _params?: ModuleInfo): Promise; - abstract signMessage(_message: Bytes | string): Promise; + abstract signMessage(_message: Bytes | string | Uint8Array): Promise; + + async signMessageWalletClientSigner(message: string | Uint8Array, signer: WalletClientSigner): Promise { + let signature: `0x${string}` = await signer.signMessage(message); + + const potentiallyIncorrectV = parseInt(signature.slice(-2), 16); + if (![27, 28].includes(potentiallyIncorrectV)) { + const correctV = potentiallyIncorrectV + 27; + signature = `0x${signature.slice(0, -2) + correctV.toString(16)}`; + } + + return signature; + } + + async signMessageSigner(message: Bytes | string, signer: Signer): Promise { + let signature = await signer.signMessage(message); + + const potentiallyIncorrectV = parseInt(signature.slice(-2), 16); + if (![27, 28].includes(potentiallyIncorrectV)) { + const correctV = potentiallyIncorrectV + 27; + signature = signature.slice(0, -2) + correctV.toString(16); + } + + return signature; + } } diff --git a/packages/modules/src/ECDSAOwnershipValidationModule.ts b/packages/modules/src/ECDSAOwnershipValidationModule.ts index 9f8d80339..613623560 100644 --- a/packages/modules/src/ECDSAOwnershipValidationModule.ts +++ b/packages/modules/src/ECDSAOwnershipValidationModule.ts @@ -4,10 +4,11 @@ import { Bytes, arrayify } from "ethers/lib/utils"; import { ECDSAOwnershipValidationModuleConfig, ModuleVersion } from "./utils/Types"; import { DEFAULT_ECDSA_OWNERSHIP_MODULE, ECDSA_OWNERSHIP_MODULE_ADDRESSES_BY_VERSION } from "./utils/Constants"; import { BaseValidationModule } from "./BaseValidationModule"; +import { WalletClientSigner } from "@alchemy/aa-core"; // Could be renamed with suffix API export class ECDSAOwnershipValidationModule extends BaseValidationModule { - signer!: Signer; + signer!: Signer | WalletClientSigner; moduleAddress!: string; @@ -40,7 +41,7 @@ export class ECDSAOwnershipValidationModule extends BaseValidationModule { return this.moduleAddress; } - async getSigner(): Promise { + async getSigner(): Promise { return Promise.resolve(this.signer); } @@ -67,15 +68,20 @@ export class ECDSAOwnershipValidationModule extends BaseValidationModule { return sig; } - async signMessage(message: Bytes | string): Promise { - let signature = await this.signer.signMessage(message); - - const potentiallyIncorrectV = parseInt(signature.slice(-2), 16); - if (![27, 28].includes(potentiallyIncorrectV)) { - const correctV = potentiallyIncorrectV + 27; - signature = signature.slice(0, -2) + correctV.toString(16); + /** + * Signs a message using the appropriate method based on the type of signer. + * + * @param {Bytes | string | Uint8Array} message - The message to be signed. + * @returns {Promise} A promise resolving to the signature or error message. + * @throws {Error} If the signer type is invalid or unsupported. + */ + async signMessage(message: Bytes | string | Uint8Array): Promise { + if (this.signer instanceof WalletClientSigner) { + return super.signMessageWalletClientSigner(message as Uint8Array | string, this.signer as WalletClientSigner); + } else if (this.signer instanceof Signer) { + return super.signMessageSigner(message as Bytes | string, this.signer as Signer); + } else { + throw new Error("Invalid signer type"); } - - return signature; } } diff --git a/packages/modules/src/MultichainValidationModule.ts b/packages/modules/src/MultichainValidationModule.ts index 082b6185d..3398fdc1c 100644 --- a/packages/modules/src/MultichainValidationModule.ts +++ b/packages/modules/src/MultichainValidationModule.ts @@ -6,8 +6,9 @@ import { DEFAULT_MULTICHAIN_MODULE, MULTICHAIN_VALIDATION_MODULE_ADDRESSES_BY_VE import { keccak256, arrayify, defaultAbiCoder, hexConcat, hexZeroPad, Bytes } from "ethers/lib/utils"; import { ModuleVersion, MultiChainUserOpDto, MultiChainValidationModuleConfig } from "./utils/Types"; import { BaseValidationModule } from "./BaseValidationModule"; +import { WalletClientSigner } from "@alchemy/aa-core"; export class MultiChainValidationModule extends BaseValidationModule { - signer!: Signer; + signer!: Signer | WalletClientSigner; moduleAddress!: string; @@ -40,7 +41,7 @@ export class MultiChainValidationModule extends BaseValidationModule { return this.moduleAddress; } - async getSigner(): Promise { + async getSigner(): Promise { return Promise.resolve(this.signer); } @@ -67,16 +68,21 @@ export class MultiChainValidationModule extends BaseValidationModule { return sig; } - async signMessage(message: Bytes | string): Promise { - let signature = await this.signer.signMessage(message); - - const potentiallyIncorrectV = parseInt(signature.slice(-2), 16); - if (![27, 28].includes(potentiallyIncorrectV)) { - const correctV = potentiallyIncorrectV + 27; - signature = signature.slice(0, -2) + correctV.toString(16); + /** + * Signs a message using the appropriate method based on the type of signer. + * + * @param {Bytes | string | Uint8Array} message - The message to be signed. + * @returns {Promise} A promise resolving to the signature or error message. + * @throws {Error} If the signer type is invalid or unsupported. + */ + async signMessage(message: Bytes | string | Uint8Array): Promise { + if (this.signer instanceof WalletClientSigner) { + return super.signMessageWalletClientSigner(message as Uint8Array | string, this.signer as WalletClientSigner); + } else if (this.signer instanceof Signer) { + return super.signMessageSigner(message as Bytes | string, this.signer as Signer); + } else { + throw new Error("Invalid signer type"); } - - return signature; } async signUserOps(multiChainUserOps: MultiChainUserOpDto[]): Promise { diff --git a/packages/modules/src/interfaces/IValidationModule.ts b/packages/modules/src/interfaces/IValidationModule.ts index 150b08cc7..4b8269674 100644 --- a/packages/modules/src/interfaces/IValidationModule.ts +++ b/packages/modules/src/interfaces/IValidationModule.ts @@ -1,10 +1,11 @@ +import { WalletClientSigner } from "@alchemy/aa-core"; import { Signer } from "ethers"; import { Bytes } from "ethers/lib/utils"; export interface IValidationModule { getAddress(): string; getInitData(): Promise; - getSigner(): Promise; + getSigner(): Promise; signUserOpHash(_userOpHash: string): Promise; signMessage(_message: Bytes | string): Promise; getDummySignature(): Promise; diff --git a/packages/modules/src/session-storage/SessionLocalStorage.ts b/packages/modules/src/session-storage/SessionLocalStorage.ts index a81fdeea0..3f9ada1c4 100644 --- a/packages/modules/src/session-storage/SessionLocalStorage.ts +++ b/packages/modules/src/session-storage/SessionLocalStorage.ts @@ -1,5 +1,5 @@ import { Wallet, Signer } from "ethers"; -import { ISessionStorage, SessionLeafNode, SessionSearchParam, SessionStatus } from "interfaces/ISessionStorage"; +import { ISessionStorage, SessionLeafNode, SessionSearchParam, SessionStatus } from "../interfaces/ISessionStorage"; export class SessionLocalStorage implements ISessionStorage { private smartAccountAddress: string; diff --git a/packages/modules/src/utils/Types.ts b/packages/modules/src/utils/Types.ts index 34c7e8f04..267de2d3a 100644 --- a/packages/modules/src/utils/Types.ts +++ b/packages/modules/src/utils/Types.ts @@ -1,7 +1,8 @@ import { ChainId, UserOperation } from "@biconomy/core-types"; import { Signer } from "ethers"; import { SessionKeyManagerModule } from "../SessionKeyManagerModule"; -import { ISessionStorage } from "interfaces/ISessionStorage"; +import { ISessionStorage } from "../interfaces/ISessionStorage"; +import { WalletClientSigner } from "@alchemy/aa-core"; export type ModuleVersion = "V1_0_0"; // | 'V1_0_1' @@ -12,7 +13,7 @@ export interface BaseValidationModuleConfig { export interface ECDSAOwnershipValidationModuleConfig extends BaseValidationModuleConfig { moduleAddress?: string; version?: ModuleVersion; - signer: Signer; + signer: Signer | WalletClientSigner; } export interface SessionKeyManagerModuleConfig extends BaseValidationModuleConfig { @@ -84,7 +85,7 @@ export interface CreateSessionDataParams { export interface MultiChainValidationModuleConfig extends BaseValidationModuleConfig { moduleAddress?: string; version?: ModuleVersion; - signer: Signer; + signer: Signer | WalletClientSigner; } export type MultiChainUserOpDto = { diff --git a/packages/node-client/CHANGELOG.md b/packages/node-client/CHANGELOG.md index e1114f85a..f1cd56de1 100644 --- a/packages/node-client/CHANGELOG.md +++ b/packages/node-client/CHANGELOG.md @@ -3,6 +3,10 @@ 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) + +VERSION Bump Only. + ## 3.1.1 (2023-11-09) Version Bump Only. diff --git a/packages/node-client/package.json b/packages/node-client/package.json index 2c25f0103..156073163 100644 --- a/packages/node-client/package.json +++ b/packages/node-client/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/node-client", - "version": "3.1.1", + "version": "3.1.2", "description": "Node Client that comminucates with indexer service to fetch necessary details for the Smart Account", "main": "./dist/src/index.js", "typings": "./dist/src/index.d.ts", @@ -66,7 +66,7 @@ "access": "public" }, "dependencies": { - "@biconomy/core-types": "^3.1.1", + "@biconomy/core-types": "^3.1.2", "@ethersproject/abstract-signer": "^5.6.0", "@nomiclabs/hardhat-ethers": "^2.1.0", "node-fetch": "^2.6.6" diff --git a/packages/particle-auth/CHANGELOG.md b/packages/particle-auth/CHANGELOG.md index 1ed5215bf..702d37b7d 100644 --- a/packages/particle-auth/CHANGELOG.md +++ b/packages/particle-auth/CHANGELOG.md @@ -3,6 +3,10 @@ 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) + +VERSION Bump Only. + ## 3.1.1 (2023-11-09) diff --git a/packages/particle-auth/package.json b/packages/particle-auth/package.json index 684c6889b..fa17d2f83 100644 --- a/packages/particle-auth/package.json +++ b/packages/particle-auth/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/particle-auth", - "version": "3.1.1", + "version": "3.1.2", "description": "Particle auth for Biconomy SDK", "main": "./dist/src/index.js", "typings": "./dist/src/index.d.ts", diff --git a/packages/paymaster/CHANGELOG.md b/packages/paymaster/CHANGELOG.md index 048cc2c64..b39368473 100644 --- a/packages/paymaster/CHANGELOG.md +++ b/packages/paymaster/CHANGELOG.md @@ -3,6 +3,10 @@ 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) + +VERSION Bump Only. + ## 3.1.1 (2023-11-09) @@ -36,7 +40,7 @@ Modular SDK - consists stable version of below updates done in Alphas. # 3.1.0-alpha.0 (2023-07-24) -VERSION bump only +VERSION Bump Only. ## 3.0.0-alpha.0 (2023-07-12) diff --git a/packages/paymaster/package.json b/packages/paymaster/package.json index bceb3fac8..25cba76c6 100644 --- a/packages/paymaster/package.json +++ b/packages/paymaster/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/paymaster", - "version": "3.1.1", + "version": "3.1.2", "description": "Biconomy Paymaster to interact with Paymaster Services that interacts with ( veriying and token ) paymasters", "main": "./dist/src/index.js", "typings": "./dist/src/index.d.ts", @@ -37,8 +37,8 @@ "access": "public" }, "dependencies": { - "@biconomy/common": "^3.1.1", - "@biconomy/core-types": "^3.1.1", + "@biconomy/common": "^3.1.2", + "@biconomy/core-types": "^3.1.2", "@ethersproject/abstract-provider": "^5.7.0", "@ethersproject/properties": "^5.7.0", "ethers": "^5.7.0" diff --git a/packages/transak/CHANGELOG.md b/packages/transak/CHANGELOG.md index a1f18fd46..a217242e3 100644 --- a/packages/transak/CHANGELOG.md +++ b/packages/transak/CHANGELOG.md @@ -3,9 +3,13 @@ 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) + +VERSION Bump Only. + ## 3.1.1 (2023-11-09) -VERSION bump only +VERSION Bump Only. @@ -22,7 +26,7 @@ VERSION bump only # 3.0.0 (2023-08-28) -VERSION bump only +VERSION Bump Only. diff --git a/packages/transak/package.json b/packages/transak/package.json index 2337ddc92..3ecdfea33 100644 --- a/packages/transak/package.json +++ b/packages/transak/package.json @@ -1,6 +1,6 @@ { "name": "@biconomy/transak", - "version": "3.1.1", + "version": "3.1.2", "description": "transak for biconomy sdk", "main": "./dist/src/index.js", "typings": "./dist/src/index.d.ts",