diff --git a/.env.example b/.env.example index b528468d..2c7c6321 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,8 @@ PRIVATE_KEY= CHAIN_ID=84532 +ALT_CHAIN_ID=11155111 RPC_URL= BUNDLER_URL= BICONOMY_SDK_DEBUG=false -RUN_PLAYGROUND=false PAYMASTER_URL= PIMLICO_API_KEY= \ No newline at end of file diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 297fb7a1..83d440bf 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -29,4 +29,5 @@ jobs: PAYMASTER_URL: ${{ secrets.PAYMASTER_URL }} BUNDLER_URL: ${{ secrets.BUNDLER_URL }} CHAIN_ID: 84532 + ALT_CHAIN_ID: 11155420 CI: true diff --git a/README.md b/README.md index 1e37fb20..e171d2fb 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,10 @@ bun add @biconomy/sdk viem @rhinestone/module-sdk 2. **Basic Usage:** ```typescript -import { createNexusClient } from "@biconomy/sdk"; +import { createSmartAccountClient } from "@biconomy/sdk"; import { http } from "viem"; -const nexusClient = await createNexusClient({ +const nexusClient = await createSmartAccountClient({ signer: account, chain, transport: http(), diff --git a/bun.lockb b/bun.lockb index a328bfd1..223adf4d 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 7e021980..dde9d7b9 100644 --- a/package.json +++ b/package.json @@ -126,8 +126,5 @@ }, "type": "module", "types": "./dist/_types/index.d.ts", - "typings": "./dist/_types/index.d.ts", - "dependencies": { - "@silencelaboratories/walletprovider-sdk": "^0.3.0" - } + "typings": "./dist/_types/index.d.ts" } \ No newline at end of file diff --git a/src/sdk/account/toNexusAccount.test.ts b/src/sdk/account/toNexusAccount.test.ts index c5260b1a..5028cd69 100644 --- a/src/sdk/account/toNexusAccount.test.ts +++ b/src/sdk/account/toNexusAccount.test.ts @@ -40,8 +40,8 @@ import { import type { MasterClient, NetworkConfig } from "../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../clients/createNexusClient" + createSmartAccountClient +} from "../clients/createSmartAccountClient" import { BICONOMY_ATTESTER_ADDRESS, MAINNET_ADDRESS_K1_VALIDATOR_FACTORY_ADDRESS, @@ -90,7 +90,7 @@ describe("nexus.account", async () => { transport: http() }) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), @@ -106,7 +106,7 @@ describe("nexus.account", async () => { }) test("should override account address", async () => { - const newNexusClient = await createNexusClient({ + const newNexusClient = await createSmartAccountClient({ chain, transport: http(), bundlerTransport: http(bundlerUrl), diff --git a/src/sdk/account/utils/Types.ts b/src/sdk/account/utils/Types.ts index 6c723a53..da108fa7 100644 --- a/src/sdk/account/utils/Types.ts +++ b/src/sdk/account/utils/Types.ts @@ -63,7 +63,6 @@ export type UserOpReceipt = { logs: Log[] } -export type Service = "Bundler" | "Paymaster" export type BigNumberish = Hex | number | bigint export type BytesLike = Uint8Array | Hex | string diff --git a/src/sdk/account/utils/Utils.ts b/src/sdk/account/utils/Utils.ts index 839c0fc3..8ec4ad5d 100644 --- a/src/sdk/account/utils/Utils.ts +++ b/src/sdk/account/utils/Utils.ts @@ -42,7 +42,7 @@ import type { AccountMetadata, EIP712DomainReturn } from "./Types" * @param value - The value to check * @returns True if the value is null or undefined */ -export const isNullOrUndefined = (value: any): value is undefined => { +export const isNullOrUndefined = (value: AnyData): value is undefined => { return value === null || value === undefined } diff --git a/src/sdk/account/utils/getAAError.ts b/src/sdk/account/utils/getAAError.ts deleted file mode 100644 index 8be4806c..00000000 --- a/src/sdk/account/utils/getAAError.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { BaseError } from "viem" -import type { Service } from ".." -export type KnownError = { - name: string - regex: string - description: string - causes: string[] - solutions: string[] - docsUrl?: string -} - -export const ERRORS_URL = - "https://raw.githubusercontent.com/bcnmy/aa-errors/main/docs/errors.json" -export const DOCS_URL = "https://docs.biconomy.io/troubleshooting/commonerrors" -const UNKOWN_ERROR_CODE = "520" - -const knownErrors: KnownError[] = [] - -const matchError = (message: string): null | KnownError => - knownErrors.find( - (knownError: KnownError) => - message.toLowerCase().indexOf(knownError.regex.toLowerCase()) > -1 - ) ?? null - -const buildErrorStrings = ( - error: KnownError, - status: string, - service?: Service -): string[] => - [ - `${status}: ${error.description}\n`, - error.causes?.length - ? ["Potential cause(s): \n", ...error.causes, ""].join("\n") - : "", - error.solutions?.length - ? ["Potential solution(s): \n", ...error.solutions].join("\n") - : "", - service ? `\nSent via: ${service}` : "" - ].filter(Boolean) - -type AccountAbstractionErrorParams = { - docsSlug?: string - metaMessages?: string[] - details?: string -} - -class AccountAbstractionError extends BaseError { - override name = "AccountAbstractionError" - override version = "@biconomy/sdk" - - constructor(title: string, params: AccountAbstractionErrorParams = {}) { - super(title, params) - } -} - -export const getAAError = async ( - message: string, - httpStatus?: number, - service?: Service -) => { - if (!knownErrors.length) { - const errors = (await (await fetch(ERRORS_URL)).json()) as KnownError[] - knownErrors.push(...errors) - } - - const details: string = - `${service} - ${typeof message}` === "string" - ? message - : JSON.stringify(message) - const matchedError = matchError(details) - const status = - matchedError?.regex ?? (httpStatus ?? UNKOWN_ERROR_CODE).toString() - - const metaMessages = matchedError - ? buildErrorStrings(matchedError, status, service) - : [] - const title = matchedError ? matchedError.name : "Unknown Error" - const docsSlug = matchedError?.docsUrl ?? DOCS_URL - - return new AccountAbstractionError(title, { - docsSlug, - metaMessages, - details - }) -} diff --git a/src/sdk/account/utils/getChain.ts b/src/sdk/account/utils/getChain.ts index 704ea76a..0184a26d 100644 --- a/src/sdk/account/utils/getChain.ts +++ b/src/sdk/account/utils/getChain.ts @@ -65,7 +65,7 @@ type StringOrStrings = string | string[] * * @example * - * import { getCustomChain, createNexusClient } from "@biconomy/sdk" + * import { getCustomChain, createSmartAccountClient } from "@biconomy/sdk" * * const customChain = getCustomChain( * "My Custom Chain", @@ -81,7 +81,7 @@ type StringOrStrings = string | string[] * transport: http() * }) * - * const smartAccountCustomChain = await createNexusClient({ + * const smartAccountCustomChain = await createSmartAccountClient({ * signer: walletClientWithCustomChain, * bundlerUrl, * customChain diff --git a/src/sdk/account/utils/toSigner.test.ts b/src/sdk/account/utils/toSigner.test.ts index 2f288e3f..34fd96c9 100644 --- a/src/sdk/account/utils/toSigner.test.ts +++ b/src/sdk/account/utils/toSigner.test.ts @@ -2,7 +2,7 @@ import { JsonRpcProvider, JsonRpcSigner, ethers } from "ethers" import { http, type Address, type Hex, createWalletClient } from "viem" import { privateKeyToAccount } from "viem/accounts" import { afterAll, beforeAll, describe, expect, it } from "vitest" -import { toNetwork } from "../../../test/testSetup" +import { toNetwork, toNetworks } from "../../../test/testSetup" import { type NetworkConfig, killNetwork, pKey } from "../../../test/testUtils" import { type EthersWallet, addressEquals } from "./Utils" import { toSigner } from "./toSigner" diff --git a/src/sdk/account/utils/utils.test.ts b/src/sdk/account/utils/utils.test.ts index 986ba514..de4e2957 100644 --- a/src/sdk/account/utils/utils.test.ts +++ b/src/sdk/account/utils/utils.test.ts @@ -7,6 +7,7 @@ import { toSigner } from "./toSigner" describe("utils", async () => { const privKey = generatePrivateKey() + test.concurrent( "should have consistent behaviour between ethers.AbiCoder.defaultAbiCoder() and viem.encodeAbiParameters()", async () => { diff --git a/src/sdk/clients/createBicoBundlerClient.test.ts b/src/sdk/clients/createBicoBundlerClient.test.ts index bab8bc0e..0e8fb9b2 100644 --- a/src/sdk/clients/createBicoBundlerClient.test.ts +++ b/src/sdk/clients/createBicoBundlerClient.test.ts @@ -45,6 +45,7 @@ describe("bico.bundler", async () => { nexusAccountAddress = await nexusAccount.getCounterFactualAddress() await topUp(testClient, nexusAccountAddress) }) + afterAll(async () => { await killNetwork([network?.rpcPort, network?.bundlerPort]) }) diff --git a/src/sdk/clients/createBicoPaymasterClient.test.ts b/src/sdk/clients/createBicoPaymasterClient.test.ts index 01e84950..18fda930 100644 --- a/src/sdk/clients/createBicoPaymasterClient.test.ts +++ b/src/sdk/clients/createBicoPaymasterClient.test.ts @@ -9,7 +9,7 @@ import { createWalletClient } from "viem" import { afterAll, beforeAll, describe, expect, test } from "vitest" -import { paymasterTruthy, toNetwork } from "../../test/testSetup" +import { paymasterTruthy, toNetworks } from "../../test/testSetup" import { getTestParamsForTestnet, killNetwork } from "../../test/testUtils" import type { NetworkConfig, TestnetParams } from "../../test/testUtils" import { type NexusAccount, toNexusAccount } from "../account/toNexusAccount" @@ -17,12 +17,15 @@ import { type BicoPaymasterClient, createBicoPaymasterClient } from "./createBicoPaymasterClient" -import { type NexusClient, createNexusClient } from "./createNexusClient" +import { + type NexusClient, + createSmartAccountClient +} from "./createSmartAccountClient" describe.runIf(paymasterTruthy())("bico.paymaster", async () => { let network: NetworkConfig - // Required for "PUBLIC_TESTNET" networks - let testParams: TestnetParams + // Required for "TESTNET_FROM_ENV_VARS" networks + let testnetParams: TestnetParams let chain: Chain let bundlerUrl: string @@ -39,7 +42,7 @@ describe.runIf(paymasterTruthy())("bico.paymaster", async () => { let nexusClient: NexusClient beforeAll(async () => { - network = await toNetwork("PUBLIC_TESTNET") + ;[network] = await toNetworks("TESTNET_FROM_ENV_VARS") chain = network.chain bundlerUrl = network.bundlerUrl @@ -59,7 +62,7 @@ describe.runIf(paymasterTruthy())("bico.paymaster", async () => { transport: http() }) - testParams = getTestParamsForTestnet(publicClient) + testnetParams = getTestParamsForTestnet(publicClient) paymaster = createBicoPaymasterClient({ transport: http(paymasterUrl) @@ -69,18 +72,18 @@ describe.runIf(paymasterTruthy())("bico.paymaster", async () => { signer: account, chain, transport: http(), - ...testParams + ...testnetParams }) nexusAccountAddress = await nexusAccount.getCounterFactualAddress() - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: account, chain, transport: http(), bundlerTransport: http(bundlerUrl), paymaster, - ...testParams + ...testnetParams }) }) afterAll(async () => { diff --git a/src/sdk/clients/createBundlerClient.test.ts b/src/sdk/clients/createBundlerClient.test.ts index 407aad3a..e05f636c 100644 --- a/src/sdk/clients/createBundlerClient.test.ts +++ b/src/sdk/clients/createBundlerClient.test.ts @@ -7,7 +7,7 @@ import { type NexusAccount, toNexusAccount } from "../account/toNexusAccount" import { safeMultiplier } from "../account/utils" import { MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS } from "../constants" import { MAINNET_ADDRESS_K1_VALIDATOR_FACTORY_ADDRESS } from "../constants" -import type { NexusClient } from "./createNexusClient" +import type { NexusClient } from "./createSmartAccountClient" import { erc7579Actions } from "./decorators/erc7579" import { smartAccountActions } from "./decorators/smartAccount" diff --git a/src/sdk/clients/createNexusClient.ts b/src/sdk/clients/createNexusClient.ts index d7c9e985..6af9af09 100644 --- a/src/sdk/clients/createNexusClient.ts +++ b/src/sdk/clients/createNexusClient.ts @@ -176,18 +176,18 @@ export type NexusClientConfig< * @returns Nexus Client. {@link NexusClient} * * @example - * import { createNexusClient } from '@biconomy/sdk' + * import { createSmartAccountClient } from '@biconomy/sdk' * import { http } from 'viem' * import { mainnet } from 'viem/chains' * - * const nexusClient = await createNexusClient({ + * const nexusClient = await createSmartAccountClient({ * chain: mainnet, * transport: http('https://mainnet.infura.io/v3/YOUR-PROJECT-ID'), * bundlerTransport: http('https://api.biconomy.io'), * signer: '0x...', * }) */ -export async function createNexusClient( +export async function createSmartAccountClient( parameters: NexusClientConfig ): Promise { const { diff --git a/src/sdk/clients/createNexusSessionClient.test.ts b/src/sdk/clients/createNexusSessionClient.test.ts index 539621d8..6643710f 100644 --- a/src/sdk/clients/createNexusSessionClient.test.ts +++ b/src/sdk/clients/createNexusSessionClient.test.ts @@ -1,5 +1,5 @@ import { SmartSessionMode } from "@rhinestone/module-sdk" -import { http, type Address, type Chain, type Hex, toBytes, toHex } from "viem" +import { http, type Address, type Chain, type Hex } from "viem" import type { LocalAccount, PublicClient } from "viem" import { encodeFunctionData } from "viem" import { afterAll, beforeAll, describe, expect, test } from "vitest" @@ -29,8 +29,10 @@ import { } from "../modules/smartSessionsValidator/decorators" import { toSmartSessionsValidator } from "../modules/smartSessionsValidator/toSmartSessionsValidator" import type { Module } from "../modules/utils/Types" -import { type NexusClient, createNexusClient } from "./createNexusClient" -import { createNexusSessionClient } from "./createNexusSessionClient" +import { + type NexusClient, + createSmartAccountClient +} from "./createSmartAccountClient" describe("nexus.session.client", async () => { let network: NetworkConfig @@ -49,7 +51,7 @@ describe("nexus.session.client", async () => { let sessionsModule: Module beforeAll(async () => { - network = await toNetwork("BASE_SEPOLIA_FORKED") + network = await toNetwork("BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA") chain = network.chain bundlerUrl = network.bundlerUrl @@ -59,7 +61,7 @@ describe("nexus.session.client", async () => { testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), @@ -181,7 +183,7 @@ describe("nexus.session.client", async () => { functionName: "getNumber" }) - const smartSessionNexusClient = await createNexusSessionClient({ + const smartSessionNexusClient = await createSmartAccountClient({ chain, accountAddress: nexusClient.account.address, signer: sessionKeyAccount, @@ -238,7 +240,7 @@ describe("nexus.session.client", async () => { moduleData: sessionData.moduleData }) - const smartSessionNexusClient = await createNexusSessionClient({ + const smartSessionNexusClient = await createSmartAccountClient({ chain, accountAddress: nexusClient.account.address, signer: sessionKeyAccount, diff --git a/src/sdk/clients/createNexusSessionClient.ts b/src/sdk/clients/createNexusSessionClient.ts deleted file mode 100644 index 01042e33..00000000 --- a/src/sdk/clients/createNexusSessionClient.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Address } from "viem" -import { type NexusClientConfig, createNexusClient } from "./createNexusClient" - -export type NexusSessionClientConfig = NexusClientConfig & { - accountAddress: Address -} -export const createNexusSessionClient = createNexusClient diff --git a/src/sdk/clients/createNexusClient.test.ts b/src/sdk/clients/createSmartAccountClient.test.ts similarity index 96% rename from src/sdk/clients/createNexusClient.test.ts rename to src/sdk/clients/createSmartAccountClient.test.ts index 08a3b7cf..5a515092 100644 --- a/src/sdk/clients/createNexusClient.test.ts +++ b/src/sdk/clients/createSmartAccountClient.test.ts @@ -32,8 +32,10 @@ import { } from "../account/utils/Utils" import { getChain } from "../account/utils/getChain" import { k1ValidatorAddress } from "../constants" -import type { AnyData } from "../modules" -import { type NexusClient, createNexusClient } from "./createNexusClient" +import { + type NexusClient, + createSmartAccountClient +} from "./createSmartAccountClient" describe("nexus.client", async () => { let network: NetworkConfig @@ -63,7 +65,7 @@ describe("nexus.client", async () => { privKey = generatePrivateKey() const account = privateKeyToAccount(privKey) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: account, chain, transport: http(), @@ -264,14 +266,14 @@ describe("nexus.client", async () => { const wallet = new Wallet(privKey) - const viemNexusClient = await createNexusClient({ + const viemNexusClient = await createSmartAccountClient({ signer: viemSigner, chain, transport: http(), bundlerTransport: http(bundlerUrl) }) - const ethersNexusClient = await createNexusClient({ + const ethersNexusClient = await createSmartAccountClient({ signer: wallet as EthersWallet, chain, transport: http(), @@ -286,7 +288,7 @@ describe("nexus.client", async () => { test("should send user operation using ethers Wallet", async () => { const ethersWallet = new ethers.Wallet(privKey) - const ethersNexusClient = await createNexusClient({ + const ethersNexusClient = await createSmartAccountClient({ signer: ethersWallet as EthersWallet, chain, transport: http(), diff --git a/src/sdk/clients/createSmartAccountClient.ts b/src/sdk/clients/createSmartAccountClient.ts new file mode 100644 index 00000000..bd010a99 --- /dev/null +++ b/src/sdk/clients/createSmartAccountClient.ts @@ -0,0 +1,250 @@ +import type { + Account, + Address, + BundlerRpcSchema, + Chain, + Client, + ClientConfig, + EstimateFeesPerGasReturnType, + LocalAccount, + OneOf, + Prettify, + RpcSchema, + Transport, + WalletClient +} from "viem" +import type { + BundlerActions, + BundlerClientConfig, + PaymasterActions, + SmartAccount, + UserOperationRequest +} from "viem/account-abstraction" + +import { + type ToNexusSmartAccountParameters, + toNexusAccount +} from "../account/toNexusAccount" +import type { EthersWallet } from "../account/utils/Utils" +import type { EthereumProvider } from "../account/utils/toSigner" +import { + k1ValidatorAddress as k1ValidatorAddress_, + k1ValidatorFactoryAddress +} from "../constants" +import type { + AnyData, + ModularSmartAccount, + Module +} from "../modules/utils/Types" +import { createBicoBundlerClient } from "./createBicoBundlerClient" +import { type Erc7579Actions, erc7579Actions } from "./decorators/erc7579" +import { + type SmartAccountActions, + smartAccountActions +} from "./decorators/smartAccount" + +/** + * Nexus Client type + */ +export type NexusClient< + transport extends Transport = Transport, + chain extends Chain | undefined = Chain | undefined, + account extends ModularSmartAccount | undefined = + | ModularSmartAccount + | undefined, + client extends Client | undefined = Client | undefined, + rpcSchema extends RpcSchema | undefined = undefined +> = Prettify< + Client< + transport, + chain extends Chain + ? chain + : client extends Client + ? chain + : undefined, + account, + rpcSchema extends RpcSchema + ? [...BundlerRpcSchema, ...rpcSchema] + : BundlerRpcSchema, + BundlerActions + > +> & + BundlerActions & + Erc7579Actions & + SmartAccountActions & { + /** + * The Nexus account associated with this client + */ + account: ModularSmartAccount + /** + * Optional client for additional functionality + */ + client?: client | Client | undefined + /** + * Transport configuration for the bundler + */ + bundlerTransport?: BundlerClientConfig["transport"] + /** + * Optional paymaster configuration + */ + paymaster?: BundlerClientConfig["paymaster"] | undefined + /** + * Optional paymaster context + */ + paymasterContext?: BundlerClientConfig["paymasterContext"] | undefined + /** + * Optional user operation configuration + */ + userOperation?: BundlerClientConfig["userOperation"] | undefined + } + +/** + * Configuration for creating a Smart account Client + */ +export type SmartAccountClientConfig< + transport extends Transport = Transport, + chain extends Chain | undefined = Chain | undefined, + client extends Client | undefined = Client | undefined, + rpcSchema extends RpcSchema | undefined = undefined +> = Prettify< + Pick< + ClientConfig, + | "account" + | "cacheTime" + | "chain" + | "key" + | "name" + | "pollingInterval" + | "rpcSchema" + > & { + /** RPC URL. */ + transport: transport + /** Bundler URL. */ + bundlerTransport: transport + /** Client that points to an Execution RPC URL. */ + client?: client | Client | undefined + /** Paymaster configuration. */ + paymaster?: + | true + | { + /** Retrieves paymaster-related User Operation properties to be used for sending the User Operation. */ + getPaymasterData?: PaymasterActions["getPaymasterData"] | undefined + /** Retrieves paymaster-related User Operation properties to be used for gas estimation. */ + getPaymasterStubData?: + | PaymasterActions["getPaymasterStubData"] + | undefined + } + | undefined + /** Paymaster context to pass to `getPaymasterData` and `getPaymasterStubData` calls. */ + paymasterContext?: unknown + /** User Operation configuration. */ + userOperation?: + | { + /** Prepares fee properties for the User Operation request. */ + estimateFeesPerGas?: + | ((parameters: { + account: SmartAccount | undefined + bundlerClient: Client + userOperation: UserOperationRequest + }) => Promise>) + | undefined + } + | undefined + /** Owner of the account. */ + signer: OneOf< + | EthereumProvider + | WalletClient + | LocalAccount + | EthersWallet + > + /** Index of the account. */ + index?: bigint + /** Active module of the account. */ + module?: Module + /** Factory address of the account. */ + factoryAddress?: Address + /** Owner module */ + k1ValidatorAddress?: Address + /** Account address */ + accountAddress?: Address + /** Attesters */ + attesters?: ToNexusSmartAccountParameters["attesters"] + /** Threshold */ + attesterThreshold?: ToNexusSmartAccountParameters["attesterThreshold"] + } +> + +/** + * Creates a Nexus Client for interacting with the Nexus smart account system. + * + * @param parameters - {@link SmartAccountClientConfig} + * @returns Nexus Client. {@link NexusClient} + * + * @example + * import { createSmartAccountClient } from '@biconomy/sdk' + * import { http } from 'viem' + * import { mainnet } from 'viem/chains' + * + * const nexusClient = await createSmartAccountClient({ + * chain: mainnet, + * transport: http('https://mainnet.infura.io/v3/YOUR-PROJECT-ID'), + * bundlerTransport: http('https://api.biconomy.io'), + * signer: '0x...', + * }) + */ +export async function createSmartAccountClient( + parameters: SmartAccountClientConfig +): Promise { + const { + account: account_, + client: client_, + chain = parameters.chain ?? client_?.chain, + signer, + index = 0n, + key = "nexus client", + name = "Nexus Client", + module, + factoryAddress = k1ValidatorFactoryAddress, + k1ValidatorAddress = k1ValidatorAddress_, + bundlerTransport, + transport, + accountAddress, + attesters, + attesterThreshold, + ...bundlerConfig + } = parameters + + if (!chain) throw new Error("Missing chain") + + const nexusAccount = + account_ ?? + (await toNexusAccount({ + accountAddress, + transport, + chain, + signer, + index, + module, + factoryAddress, + k1ValidatorAddress, + attesters, + attesterThreshold + })) + + const bundler_ = createBicoBundlerClient({ + ...bundlerConfig, + chain, + key, + name, + account: nexusAccount, + transport: bundlerTransport + }) + .extend(erc7579Actions()) + .extend(smartAccountActions()) + + return bundler_ as unknown as NexusClient +} + +// Aliases for backwards compatibility +export const createNexusClient = createSmartAccountClient +export const createNexusSessionClient = createSmartAccountClient diff --git a/src/sdk/clients/decorators/erc7579/erc7579.decorators.test.ts b/src/sdk/clients/decorators/erc7579/erc7579.decorators.test.ts index 24ee7731..2086d9ec 100644 --- a/src/sdk/clients/decorators/erc7579/erc7579.decorators.test.ts +++ b/src/sdk/clients/decorators/erc7579/erc7579.decorators.test.ts @@ -18,7 +18,10 @@ import { toTestClient } from "../../../../test/testUtils" import { k1ValidatorAddress } from "../../../constants" -import { type NexusClient, createNexusClient } from "../../createNexusClient" +import { + type NexusClient, + createSmartAccountClient +} from "../../createSmartAccountClient" describe("erc7579.decorators", async () => { let network: NetworkConfig @@ -43,7 +46,7 @@ describe("erc7579.decorators", async () => { recipientAddress = recipient.address testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), diff --git a/src/sdk/clients/decorators/smartAccount/account.decorators.test.ts b/src/sdk/clients/decorators/smartAccount/account.decorators.test.ts index e217d341..cad45cac 100644 --- a/src/sdk/clients/decorators/smartAccount/account.decorators.test.ts +++ b/src/sdk/clients/decorators/smartAccount/account.decorators.test.ts @@ -12,7 +12,10 @@ import { killNetwork, toTestClient } from "../../../../test/testUtils" -import { type NexusClient, createNexusClient } from "../../createNexusClient" +import { + type NexusClient, + createSmartAccountClient +} from "../../createSmartAccountClient" describe("account.decorators", async () => { let network: NetworkConfig @@ -37,7 +40,7 @@ describe("account.decorators", async () => { recipientAddress = recipient.address testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), diff --git a/src/sdk/clients/index.ts b/src/sdk/clients/index.ts index 4e9aadde..4022c530 100644 --- a/src/sdk/clients/index.ts +++ b/src/sdk/clients/index.ts @@ -1,5 +1,4 @@ export * from "./createBicoBundlerClient" export * from "./createBicoPaymasterClient" -export * from "./createNexusClient" -export * from "./createNexusSessionClient" +export * from "./createSmartAccountClient" export * from "./decorators" diff --git a/src/sdk/constants/index.ts b/src/sdk/constants/index.ts index 27261634..1004c916 100644 --- a/src/sdk/constants/index.ts +++ b/src/sdk/constants/index.ts @@ -5,7 +5,7 @@ import { getUsageLimitPolicy, getValueLimitPolicy } from "@rhinestone/module-sdk" -import { type Hex, getAddress, toBytes, toHex } from "viem" +import { type Hex, toBytes, toHex } from "viem" import { isTesting } from "../account" import { ParamCondition } from "../modules/smartSessionsValidator/Types" diff --git a/src/sdk/modules/k1Validator/toK1Validator.test.ts b/src/sdk/modules/k1Validator/toK1Validator.test.ts index 8c62b944..53425a95 100644 --- a/src/sdk/modules/k1Validator/toK1Validator.test.ts +++ b/src/sdk/modules/k1Validator/toK1Validator.test.ts @@ -18,8 +18,8 @@ import { import type { MasterClient, NetworkConfig } from "../../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../../clients/createNexusClient" + createSmartAccountClient +} from "../../clients/createSmartAccountClient" import { k1ValidatorAddress } from "../../constants" import { toK1Validator } from "./toK1Validator" @@ -47,7 +47,7 @@ describe("modules.k1Validator", async () => { testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), diff --git a/src/sdk/modules/ownableValidator/decorators/getThreshold.ts b/src/sdk/modules/ownableValidator/decorators/getThreshold.ts index dce90013..a81d05f9 100644 --- a/src/sdk/modules/ownableValidator/decorators/getThreshold.ts +++ b/src/sdk/modules/ownableValidator/decorators/getThreshold.ts @@ -36,7 +36,7 @@ export type GetThresholdParameters< * * @example * ```typescript - * const nexusClient = createNexusClient({ ... }); + * const nexusClient = createSmartAccountClient({ ... }); * const threshold = await getThreshold(nexusClient); * console.log(`Current approval threshold: ${threshold}`); * ``` diff --git a/src/sdk/modules/ownableValidator/decorators/ownables.decorators.test.ts b/src/sdk/modules/ownableValidator/decorators/ownables.decorators.test.ts index b77c1dba..3abe77c2 100644 --- a/src/sdk/modules/ownableValidator/decorators/ownables.decorators.test.ts +++ b/src/sdk/modules/ownableValidator/decorators/ownables.decorators.test.ts @@ -19,8 +19,8 @@ import { } from "../../../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../../../clients/createNexusClient" + createSmartAccountClient +} from "../../../clients/createSmartAccountClient" import { toOwnableValidator } from "../toOwnableValidator" describe("modules.ownables.decorators", async () => { @@ -50,7 +50,7 @@ describe("modules.ownables.decorators", async () => { userThreeAddress = userThree.address testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), diff --git a/src/sdk/modules/ownableValidator/decorators/setThreshold.ts b/src/sdk/modules/ownableValidator/decorators/setThreshold.ts index 5a5e20aa..d2e46f9d 100644 --- a/src/sdk/modules/ownableValidator/decorators/setThreshold.ts +++ b/src/sdk/modules/ownableValidator/decorators/setThreshold.ts @@ -40,7 +40,7 @@ export type SetThresholdParameters< * * @example * ```typescript - * const nexusClient = createNexusClient({ ... }); + * const nexusClient = createSmartAccountClient({ ... }); * const hash = await setThreshold(nexusClient, { * threshold: 2, * maxFeePerGas: 1000000000n diff --git a/src/sdk/modules/ownableValidator/toOwnableValidator.dx.test.ts b/src/sdk/modules/ownableValidator/toOwnableValidator.dx.test.ts index 60782c90..6c3c3d6a 100644 --- a/src/sdk/modules/ownableValidator/toOwnableValidator.dx.test.ts +++ b/src/sdk/modules/ownableValidator/toOwnableValidator.dx.test.ts @@ -8,7 +8,7 @@ import { toTestClient } from "../../../test/testUtils" import type { MasterClient, NetworkConfig } from "../../../test/testUtils" -import { createNexusClient } from "../../clients/createNexusClient" +import { createSmartAccountClient } from "../../clients/createSmartAccountClient" import { ownableActions } from "./decorators" import { toOwnableValidator } from "./toOwnableValidator" @@ -59,7 +59,7 @@ describe("modules.ownableValidator.dx", async () => { // Create a Nexus client for the main account (eoaAccount) // This client will be used to interact with the smart contract account - const nexusClient = await createNexusClient({ + const nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), diff --git a/src/sdk/modules/ownableValidator/toOwnableValidator.executor.test.ts b/src/sdk/modules/ownableValidator/toOwnableValidator.executor.test.ts index 48c2c3c4..b606f559 100644 --- a/src/sdk/modules/ownableValidator/toOwnableValidator.executor.test.ts +++ b/src/sdk/modules/ownableValidator/toOwnableValidator.executor.test.ts @@ -28,8 +28,8 @@ import { import type { MasterClient, NetworkConfig } from "../../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../../clients/createNexusClient" + createSmartAccountClient +} from "../../clients/createSmartAccountClient" import { moduleActivator } from "../../clients/decorators/erc7579/moduleActivator" import { toK1Validator } from "../k1Validator/toK1Validator" import type { Module } from "../utils/Types" @@ -58,7 +58,7 @@ describe("modules.ownableExecutor", async () => { testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), diff --git a/src/sdk/modules/ownableValidator/toOwnableValidator.test.ts b/src/sdk/modules/ownableValidator/toOwnableValidator.test.ts index 8724302d..c4e1a25a 100644 --- a/src/sdk/modules/ownableValidator/toOwnableValidator.test.ts +++ b/src/sdk/modules/ownableValidator/toOwnableValidator.test.ts @@ -24,8 +24,8 @@ import type { MasterClient, NetworkConfig } from "../../../test/testUtils" import type { NexusAccount } from "../../account" import { type NexusClient, - createNexusClient -} from "../../clients/createNexusClient" + createSmartAccountClient +} from "../../clients/createSmartAccountClient" import { parseModuleTypeId } from "../../clients/decorators/erc7579/supportsModule" import { k1ValidatorAddress } from "../../constants" import type { Module } from "../utils/Types" @@ -61,7 +61,7 @@ describe("modules.ownables", async () => { userThreeAddress = userThree.address testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), diff --git a/src/sdk/modules/smartSessionsValidator/decorators/smartSessions.decorators.test.ts b/src/sdk/modules/smartSessionsValidator/decorators/smartSessions.decorators.test.ts index c766c4db..30325c11 100644 --- a/src/sdk/modules/smartSessionsValidator/decorators/smartSessions.decorators.test.ts +++ b/src/sdk/modules/smartSessionsValidator/decorators/smartSessions.decorators.test.ts @@ -14,9 +14,8 @@ import { } from "../../../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../../../clients/createNexusClient" -import { createNexusSessionClient } from "../../../clients/createNexusSessionClient" + createSmartAccountClient +} from "../../../clients/createSmartAccountClient" import type { CreateSessionDataParams } from "../Types" import { type SmartSessionModule, @@ -50,6 +49,13 @@ describe("modules.smartSessions.decorators", async () => { sessionPublicKey = sessionKeyAccount.address testClient = toTestClient(chain, getTestAccount(5)) + nexusClient = await createSmartAccountClient({ + signer: eoaAccount, + chain, + transport: http(), + bundlerTransport: http(bundlerUrl) + }) + sessionRequestedInfo = [ { sessionPublicKey, // Public key of the session @@ -68,13 +74,6 @@ describe("modules.smartSessions.decorators", async () => { } ] - nexusClient = await createNexusClient({ - signer: eoaAccount, - chain, - transport: http(), - bundlerTransport: http(bundlerUrl) - }) - nexusAccountAddress = await nexusClient.account.getCounterFactualAddress() await fundAndDeployClients(testClient, [nexusClient]) }) @@ -112,7 +111,7 @@ describe("modules.smartSessions.decorators", async () => { signer: sessionKeyAccount }) - const smartSessionNexusClient = await createNexusSessionClient({ + const smartSessionNexusClient = await createSmartAccountClient({ chain, accountAddress: nexusClient.account.address, signer: sessionKeyAccount, diff --git a/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.enable.mode.test.ts b/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.enable.mode.test.ts index dd7845cc..10428b93 100644 --- a/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.enable.mode.test.ts +++ b/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.enable.mode.test.ts @@ -19,9 +19,9 @@ import { createPublicClient, createWalletClient, encodeFunctionData, + getAddress, toBytes, - toHex, - getAddress + toHex } from "viem" import { entryPoint07Address, @@ -32,13 +32,7 @@ import { afterAll, beforeAll, describe, expect, test } from "vitest" import { CounterAbi } from "../../../test/__contracts/abi/CounterAbi" import { testAddresses } from "../../../test/callDatas" import { toNetwork } from "../../../test/testSetup" -import { - fundAndDeployClients, - getTestAccount, - getTestParamsForTestnet, - killNetwork, - toTestClient -} from "../../../test/testUtils" +import { getTestParamsForTestnet } from "../../../test/testUtils" import type { MasterClient, NetworkConfig, @@ -47,14 +41,14 @@ import type { import { type NexusAccount, toNexusAccount } from "../../account/toNexusAccount" import { type NexusClient, - createNexusClient -} from "../../clients/createNexusClient" + createSmartAccountClient +} from "../../clients/createSmartAccountClient" import { SIMPLE_SESSION_VALIDATOR_ADDRESS } from "../../constants" import { generateSalt } from "./Helpers" describe("modules.smartSessions.enable.mode.dx", async () => { let network: NetworkConfig - // Required for "PUBLIC_TESTNET" networks + // Required for "TESTNET_FROM_ENV_VARS" networks let testParams: TestnetParams let chain: Chain @@ -74,7 +68,7 @@ describe("modules.smartSessions.enable.mode.dx", async () => { let sessionPublicKey: Address beforeAll(async () => { - network = await toNetwork("PUBLIC_TESTNET") + network = await toNetwork("TESTNET_FROM_ENV_VARS") chain = network.chain bundlerUrl = network.bundlerUrl @@ -108,7 +102,7 @@ describe("modules.smartSessions.enable.mode.dx", async () => { nexusAccountAddress = await nexusAccount.getCounterFactualAddress() - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ account: nexusAccount, signer: eoaAccount, chain, diff --git a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.dx.test.ts b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.dx.test.ts index 7987b893..bc5ff3fa 100644 --- a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.dx.test.ts +++ b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.dx.test.ts @@ -21,9 +21,8 @@ import { import type { MasterClient, NetworkConfig } from "../../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../../clients/createNexusClient" -import { createNexusSessionClient } from "../../clients/createNexusSessionClient" + createSmartAccountClient +} from "../../clients/createSmartAccountClient" import type { Module } from "../utils/Types" import { abiToPoliciesInfo, parse, stringify } from "./Helpers" import type { CreateSessionDataParams, SessionData } from "./Types" @@ -46,7 +45,7 @@ describe("modules.smartSessions.dx", async () => { let sessionsModule: Module beforeAll(async () => { - network = await toNetwork("BASE_SEPOLIA_FORKED") + network = await toNetwork("BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA") chain = network.chain bundlerUrl = network.bundlerUrl @@ -81,7 +80,7 @@ describe("modules.smartSessions.dx", async () => { // Create a Nexus client for the main account (eoaAccount) // This client will be used to interact with the smart contract account - usersNexusClient = await createNexusClient({ + usersNexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), @@ -168,7 +167,7 @@ describe("modules.smartSessions.dx", async () => { // Create a new Nexus client for the session // This client will be used to interact with the smart contract account using the session key - const smartSessionNexusClient = await createNexusSessionClient({ + const smartSessionNexusClient = await createSmartAccountClient({ chain, accountAddress: usersSessionData.granter, signer: sessionKeyAccount, diff --git a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.policies.test.ts b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.policies.test.ts index 531b6b2e..031ad57d 100644 --- a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.policies.test.ts +++ b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.policies.test.ts @@ -18,8 +18,8 @@ import { import type { MasterClient, NetworkConfig } from "../../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../../clients/createNexusClient" + createSmartAccountClient +} from "../../clients/createSmartAccountClient" import type { Module } from "../utils/Types" import { smartSessionCreateActions } from "./decorators" import { toSmartSessionsValidator } from "./toSmartSessionsValidator" @@ -40,7 +40,7 @@ describe("modules.smartSessions.policies", async () => { let sessionsModule: Module beforeAll(async () => { - network = await toNetwork("BASE_SEPOLIA_FORKED") + network = await toNetwork("BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA") chain = network.chain bundlerUrl = network.bundlerUrl @@ -50,7 +50,7 @@ describe("modules.smartSessions.policies", async () => { testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), @@ -93,7 +93,7 @@ describe("modules.smartSessions.policies", async () => { }) test("should grant permission with all available policies", async () => { - const usersNexusClient = await createNexusClient({ + const usersNexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), diff --git a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.sudo.policy.test.ts b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.sudo.policy.test.ts index 3aeda2a9..8155d2c6 100644 --- a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.sudo.policy.test.ts +++ b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.sudo.policy.test.ts @@ -23,8 +23,8 @@ import { import type { MasterClient, NetworkConfig } from "../../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../../clients/createNexusClient" + createSmartAccountClient +} from "../../clients/createSmartAccountClient" import type { Module } from "../utils/Types" import { parse, stringify } from "./Helpers" import type { SessionData } from "./Types" @@ -49,7 +49,7 @@ describe("modules.smartSessions.sudo.policy", async () => { let sessionsModule: Module beforeAll(async () => { - network = await toNetwork("BASE_SEPOLIA_FORKED") + network = await toNetwork("BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA") chain = network.chain bundlerUrl = network.bundlerUrl @@ -59,7 +59,7 @@ describe("modules.smartSessions.sudo.policy", async () => { testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), @@ -151,7 +151,7 @@ describe("modules.smartSessions.sudo.policy", async () => { // Create a new Nexus client for the session // This client will be used to interact with the smart contract account using the session key - const smartSessionNexusClient = await createNexusClient({ + const smartSessionNexusClient = await createSmartAccountClient({ chain, accountAddress: usersSessionData.granter, signer: sessionKeyAccount, diff --git a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.test.ts b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.test.ts index f8997e64..d2de49e4 100644 --- a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.test.ts +++ b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.test.ts @@ -23,9 +23,8 @@ import { import type { MasterClient, NetworkConfig } from "../../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../../clients/createNexusClient" -import { createNexusSessionClient } from "../../clients/createNexusSessionClient" + createSmartAccountClient +} from "../../clients/createSmartAccountClient" import { parseReferenceValue } from "../utils/Helpers" import type { Module } from "../utils/Types" import { @@ -55,7 +54,7 @@ describe("modules.smartSessions", async () => { let sessionsModule: Module beforeAll(async () => { - network = await toNetwork("BASE_SEPOLIA_FORKED") + network = await toNetwork("BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA") chain = network.chain bundlerUrl = network.bundlerUrl @@ -64,7 +63,7 @@ describe("modules.smartSessions", async () => { sessionPublicKey = sessionKeyAccount.address testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), @@ -280,7 +279,7 @@ describe("modules.smartSessions", async () => { const parsedSessionData = parse(cachedSessionData) as SessionData - const smartSessionNexusClient = await createNexusSessionClient({ + const smartSessionNexusClient = await createSmartAccountClient({ chain, accountAddress: nexusClient.account.address, signer: sessionKeyAccount, diff --git a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.ts b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.ts index 96a5f33b..11a6b18c 100644 --- a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.ts +++ b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.ts @@ -14,7 +14,6 @@ export const DUMMY_ECDSA_SIG = "0xe8b94748580ca0b4993c9a1b86b5be851bfc076ff5ce3a1ff65bf16392acfcb800f9b4f1aef1555c7fce5599fffb17e7c635502154a0333ba21f3ae491839af51c" export type SmartSessionModule = Module & { - sigGen: (signature: Hex) => Hex moduleData?: UsePermissionModuleData } diff --git a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.uni.policy.test.ts b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.uni.policy.test.ts index b6c61db4..b4b4a7d7 100644 --- a/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.uni.policy.test.ts +++ b/src/sdk/modules/smartSessionsValidator/toSmartSessionsValidator.uni.policy.test.ts @@ -27,9 +27,8 @@ import { import type { MasterClient, NetworkConfig } from "../../../test/testUtils" import { type NexusClient, - createNexusClient -} from "../../clients/createNexusClient" -import { createNexusSessionClient } from "../../clients/createNexusSessionClient" + createSmartAccountClient +} from "../../clients/createSmartAccountClient" import { SMART_SESSIONS_ADDRESS } from "../../constants" import type { Module } from "../utils/Types" import { isPermissionEnabled, parse, stringify } from "./Helpers" @@ -55,7 +54,7 @@ describe("modules.smartSessions.uni.policy", async () => { let sessionsModule: Module beforeAll(async () => { - network = await toNetwork("BASE_SEPOLIA_FORKED") + network = await toNetwork("BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA") chain = network.chain bundlerUrl = network.bundlerUrl @@ -65,7 +64,7 @@ describe("modules.smartSessions.uni.policy", async () => { testClient = toTestClient(chain, getTestAccount(5)) - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), @@ -287,7 +286,7 @@ describe("modules.smartSessions.uni.policy", async () => { // timestamp: 9727001666n // }) - const smartSessionNexusClient = await createNexusSessionClient({ + const smartSessionNexusClient = await createSmartAccountClient({ chain, accountAddress: nexusClient.account.address, signer: sessionKeyAccount, diff --git a/src/sdk/modules/utils/Types.ts b/src/sdk/modules/utils/Types.ts index f040d356..b8e45721 100644 --- a/src/sdk/modules/utils/Types.ts +++ b/src/sdk/modules/utils/Types.ts @@ -1,5 +1,6 @@ import type { Address, Assign, Chain, Hex, SignableMessage } from "viem" import type { SmartAccount } from "viem/account-abstraction" +import type { NexusSmartAccountImplementation } from "../../account/toNexusAccount" import type { Signer } from "./../../account/utils/toSigner" export type ModuleVersion = "1.0.0" // | 'V1_0_1' @@ -116,7 +117,8 @@ export type Modularity = { setModule: (module: Module) => void } -export type ModularSmartAccount = SmartAccount & Modularity +export type ModularSmartAccount = + SmartAccount & Modularity export type ModuleMeta = { address: Hex diff --git a/src/sdk/modules/utils/toModule.test.ts b/src/sdk/modules/utils/toModule.test.ts index 1095d6a8..b24cd87a 100644 --- a/src/sdk/modules/utils/toModule.test.ts +++ b/src/sdk/modules/utils/toModule.test.ts @@ -1,11 +1,4 @@ -import { - http, - type Account, - type Address, - type Chain, - type LocalAccount, - isHex -} from "viem" +import type { Chain, LocalAccount } from "viem" import { afterAll, beforeAll, describe, expect, test } from "vitest" import { toNetwork } from "../../../test/testSetup" import { diff --git a/src/test/README.md b/src/test/README.md index 0cf7d717..d2698a56 100644 --- a/src/test/README.md +++ b/src/test/README.md @@ -9,71 +9,21 @@ - Tests are executed against locally deployed ephemeral Anvil chains (each with a different ID) with relevant contracts pre-deployed for each test. - Bundlers for testing are instantiated using [prool](https://github.com/wevm/prool), currently utilizing alto instances. We plan to switch to Biconomy's bundlers when they become available via `prool`. -### Deployment Configuration -A custom script `bun run fetch:deployment` is provided to search for the bytecode of deployed contracts from a customizable location (default: `../../nexus/deployments`). This folder is **auto-generated** in Nexus whenever a new Hardhat deployment is made, ensuring that the SDK remains up-to-date with the latest contract changes. - -The script performs the following: -- **ABIs**: Moved to `./src/__contracts/{name}Abi.ts` -- **Addresses**: Moved to `./src/addresses.ts` -- **Additional Fixtures**: Copied to `tests__/contracts` - -The script accepts a number of args from the command line: - - nexusDeploymentPath (default: `"../node_modules/nexus/deployments"`) - - chainName (default: `"anvil-55000"`) - - forSrc (default: `["K1ValidatorFactory", "Nexus", "K1Validator"]`); - -Example usage: -```bash -bun run fetch:deployment:raw --chainName=anvil-52878 -forSrc=K1Validator -forSrc=Nexus --nexusDeploymentPath=../../nexus/deployments -bun run lint --apply-unsafe -``` - -> **Note**: -> - Do not edit these files manually; they will be overridden if/when a new Nexus deployment occurs. -> - Avoid hardcoding important addresses (e.g., `const k1ValidatorAddress = "0x"`). Use `./src/addresses.ts` instead. - -## Network Scopes for Tests - -To prevent tests from conflicting with one another, tests can be scoped to different networks in different ways. - ### Global Scope -- Use by setting `const NETWORK_TYPE: TestFileNetworkType = "COMMON_LOCALHOST"` at the top of the test file. +- Use by setting `const NETWORK_TYPE: TestFileNetworkType = "COMMUNAL_ANVIL_NETWORK"` at the top of the test file. - Suitable when you're sure that tests in the file will **not** conflict with other tests using the common localhost network. ### Local Scope -- Use by setting `const NETWORK_TYPE: TestFileNetworkType = "FILE_LOCALHOST"` for test files that may conflict with others. +- Use by setting `const NETWORK_TYPE: TestFileNetworkType = "BESPOKE_ANVIL_NETWORK"` for test files that may conflict with others. - Networks scoped locally are isolated to the file in which they are used. - Tests within the same file using a local network may conflict with each other. If needed, split tests into separate files or use the Test Scope. +- `"BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA"` does a similar thing, but anvil assumes the current state of base sepolia instead. Overusing this can cause throttling issues from tenderly or the service that you are finding the forkUrl from. -### Test Scope -- A network is spun up *only* for the individual test in which it is used. Access this via the `localhostTest`/`testnetTest` helpers in the same file as `"COMMON_LOCALHOST"` or `"FILE_LOCALHOST"` network types. - -Example usage: -```ts -localhostTest("should be used in the following way", async({ config: { bundlerUrl, chain, fundedClients }}) => { - // chain, bundlerUrl spun up just in time for this test only... - expect(await fundedClients.smartAccount.getAccountAddress()).toBeTruthy(); -}); -``` - -> **Note:** -> Please avoid using multiple nested describe blocks in a single test file, as it is unnecessary and can lead to confusion regarding network scope. -> Using *many* test files is preferable, as describe blocks run in parallel. - -## Testing on Testnets or New Chains -- There is currently one area where SDK tests can be run against a remote testnet: the playground -- You can run the playground using the command: `bun run playground`. They playground is automatically ommitted from CICD. -- Additionally there are helpers for running tests on files on a public testnet: - - `const NETWORK_TYPE: TestFileNetworkType = "TESTNET"` will pick up relevant configuration from environment variables, and can be used at the top of a test file to have tests run against the specified testnet instead of the localhost - - If you want to run a single test on a public testnet *from inside a different describe block* you can use the: `testnetTest` helper: - -Example usage: -```ts -testnetTest("should be used in the following way", async({ config: { bundlerUrl, chain, account }}) => { - // chain, bundlerUrl etc taken from environment variables... - expect(account).toBeTruthy(); // from private key, please ensure it is funded if sending txs -}); -``` +### Testnet Scope +- Use by setting `const NETWORK_TYPE: TestFileNetworkType = "TESTNET_FROM_ENV_VARS"` for test files that rely on the network, private key and bundler url specified in your env vars. +- `"TESTNET_FROM_ALT_ENV_VARS"` is also available, which uses alternative env vars which you've specified. +- Networks scoped to a testnet are not isolated to the file in which they are used, they require tesnet tokens, can often fail for gas reasons, and they will add additional latency to tests. +- Avoid overusing this option > **Note:** > As testnetTest runs against a public testnet the account related to the privatekey (in your env var) must be funded, and the testnet is not 'ephemeral', meaning state is obviously persisted on the testnet after the test finishes. diff --git a/src/test/playground.test.ts b/src/test/playground.test.ts index 2380c8d2..740128f9 100644 --- a/src/test/playground.test.ts +++ b/src/test/playground.test.ts @@ -17,9 +17,8 @@ import { playgroundTrue } from "../sdk/account/utils/Utils" import { createBicoPaymasterClient } from "../sdk/clients/createBicoPaymasterClient" import { type NexusClient, - createNexusClient -} from "../sdk/clients/createNexusClient" -import { MAINNET_ADDRESS_K1_VALIDATOR_FACTORY_ADDRESS } from "../sdk/constants" + createSmartAccountClient +} from "../sdk/clients/createSmartAccountClient" import type { CreateSessionDataParams, SessionData @@ -40,7 +39,7 @@ import { describe.skipIf(!playgroundTrue())("playground", () => { let network: NetworkConfig - // Required for "PUBLIC_TESTNET" networks + // Required for "TESTNET_FROM_ENV_VARS" networks let testParams: TestnetParams // Nexus Config let chain: Chain @@ -55,7 +54,7 @@ describe.skipIf(!playgroundTrue())("playground", () => { let nexusAccountAddress: Address beforeAll(async () => { - network = await toNetwork("PUBLIC_TESTNET") + network = await toNetwork("TESTNET_FROM_ENV_VARS") chain = network.chain bundlerUrl = network.bundlerUrl @@ -78,7 +77,7 @@ describe.skipIf(!playgroundTrue())("playground", () => { }) test("should init the smart account", async () => { - nexusClient = await createNexusClient({ + nexusClient = await createSmartAccountClient({ signer: eoaAccount, chain, transport: http(), @@ -171,7 +170,8 @@ describe.skipIf(!playgroundTrue())("playground", () => { expect(balanceAfter - balanceBefore).toBe(1n) }) - test("should test creating and using a session", async () => { + // Skipped because on base sepolia the attestations for smart sessions have not been created yet + test.skip("should test creating and using a session", async () => { const sessionsModule = toSmartSessionsValidator({ account: nexusClient.account, signer: eoaAccount @@ -229,12 +229,13 @@ describe.skipIf(!playgroundTrue())("playground", () => { granter: nexusClient.account.address, sessionPublicKey: eoaAccount.address, moduleData: { - ...createSessionsResponse, + permissionIds: createSessionsResponse.permissionIds, + action: createSessionsResponse.action, mode: SmartSessionMode.USE } } - const smartSessionNexusClient = await createNexusClient({ + const smartSessionNexusClient = await createSmartAccountClient({ chain, accountAddress: nexusClient.account.address, signer: eoaAccount, diff --git a/src/test/testSetup.ts b/src/test/testSetup.ts index 41f3eaef..7bc74a8e 100644 --- a/src/test/testSetup.ts +++ b/src/test/testSetup.ts @@ -35,26 +35,46 @@ export const testnetTest = test.extend<{ }>({ // biome-ignore lint/correctness/noEmptyPattern: Needed in vitest :/ config: async ({}, use) => { - const testNetwork = await toNetwork("PUBLIC_TESTNET") + const testNetwork = await toNetwork("TESTNET_FROM_ENV_VARS") await use(testNetwork) } }) export type TestFileNetworkType = - | "FILE_LOCALHOST" - | "COMMON_LOCALHOST" - | "PUBLIC_TESTNET" - | "BASE_SEPOLIA_FORKED" + | "BESPOKE_ANVIL_NETWORK" + | "BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA" + | "TESTNET_FROM_ENV_VARS" + | "TESTNET_FROM_ALT_ENV_VARS" + | "COMMUNAL_ANVIL_NETWORK" + +export const toNetworks = async ( + networkTypes_: TestFileNetworkType | TestFileNetworkType[] = [ + "BESPOKE_ANVIL_NETWORK" + ] +): Promise => { + const networkTypes = Array.isArray(networkTypes_) + ? networkTypes_ + : [networkTypes_] + + return await Promise.all(networkTypes.map((type) => toNetwork(type))) +} export const toNetwork = async ( - networkType: TestFileNetworkType = "FILE_LOCALHOST" + networkType: TestFileNetworkType = "BESPOKE_ANVIL_NETWORK" ): Promise => { - const forkBaseSepolia = networkType === "BASE_SEPOLIA_FORKED" - return await (networkType === "COMMON_LOCALHOST" + const forkBaseSepolia = + networkType === "BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA" + const communalAnvil = networkType === "COMMUNAL_ANVIL_NETWORK" + const testNet = [ + "TESTNET_FROM_ENV_VARS", + "TESTNET_FROM_ALT_ENV_VARS" + ].includes(networkType) + + return await (communalAnvil ? // @ts-ignore inject("globalNetwork") - : networkType === "PUBLIC_TESTNET" - ? initTestnetNetwork() + : testNet + ? initTestnetNetwork(networkType) : initLocalhostNetwork(forkBaseSepolia)) } diff --git a/src/test/testUtils.ts b/src/test/testUtils.ts index 770aaab7..5b2978b6 100644 --- a/src/test/testUtils.ts +++ b/src/test/testUtils.ts @@ -1,6 +1,5 @@ import { config } from "dotenv" import getPort from "get-port" -// @ts-ignore import { type AnvilParameters, alto, anvil } from "prool/instances" import { http, @@ -22,10 +21,6 @@ import { createBundlerClient } from "viem/account-abstraction" import { mnemonicToAccount, privateKeyToAccount } from "viem/accounts" import { getChain, getCustomChain, safeMultiplier } from "../sdk/account/utils" import { Logger } from "../sdk/account/utils/Logger" -import { - type NexusClient, - createNexusClient -} from "../sdk/clients/createNexusClient" import { ENTRYPOINT_SIMULATIONS_ADDRESS, ENTRY_POINT_ADDRESS, @@ -38,7 +33,12 @@ import { TEST_CONTRACTS } from "./callDatas" +import { + type NexusClient, + createSmartAccountClient +} from "../sdk/clients/createSmartAccountClient" import * as hardhatExec from "./executables" +import type { TestFileNetworkType } from "./testSetup" config() @@ -62,6 +62,7 @@ export type NetworkConfig = Omit< > & { account?: PrivateKeyAccount paymasterUrl?: string + meeNodeUrl?: string } export const pKey = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" // This is a publicly available private key meant only for testing only @@ -93,13 +94,18 @@ export const killNetwork = (ids: number[]) => }) ) -export const initTestnetNetwork = async (): Promise => { +export const initTestnetNetwork = async ( + type: TestFileNetworkType = "TESTNET_FROM_ENV_VARS" +): Promise => { const privateKey = process.env.PRIVATE_KEY - const chainId = process.env.CHAIN_ID + const chainId_ = process.env.CHAIN_ID + const altChainId = process.env.ALT_CHAIN_ID const rpcUrl = process.env.RPC_URL //Optional, taken from chain (using chainId) if not provided const _bundlerUrl = process.env.BUNDLER_URL // Optional, taken from chain (using chainId) if not provided const paymasterUrl = process.env.PAYMASTER_URL // Optional + const chainId = type === "TESTNET_FROM_ALT_ENV_VARS" ? altChainId : chainId_ + let chain: Chain if (!privateKey) throw new Error("Missing env var PRIVATE_KEY") @@ -125,7 +131,8 @@ export const initTestnetNetwork = async (): Promise => { bundlerUrl, paymasterUrl, bundlerPort: 0, - account: holder + account: holder, + meeNodeUrl } } @@ -318,7 +325,7 @@ export const toFundedTestClients = async ({ const testClient = toTestClient(chain, getTestAccount()) - const nexus = await createNexusClient({ + const nexus = await createSmartAccountClient({ signer: account, transport: http(), bundlerTransport: http(bundlerUrl),