diff --git a/src/sdk/account/toNexusAccount.ts b/src/sdk/account/toNexusAccount.ts index 1f007a0cc..fdcd2622f 100644 --- a/src/sdk/account/toNexusAccount.ts +++ b/src/sdk/account/toNexusAccount.ts @@ -396,7 +396,7 @@ export const toNexusAccount = async ( /** * @description Gets the nonce for the account - * @param args - Optional arguments for getting the nonce + * @param parameters - Optional parameters for getting the nonce * @returns The nonce */ const getNonce = async (parameters?: { diff --git a/src/sdk/clients/createNexusClient.ts b/src/sdk/clients/createNexusClient.ts deleted file mode 100644 index 6af9af099..000000000 --- a/src/sdk/clients/createNexusClient.ts +++ /dev/null @@ -1,241 +0,0 @@ -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 NexusAccount, - 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, 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 NexusAccount | undefined = NexusAccount | 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: NexusAccount - /** - * 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 Nexus Client - */ -export type NexusClientConfig< - 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 NexusClientConfig} - * @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: NexusClientConfig -): 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 -} diff --git a/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.enable.mode.test.ts b/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.enable.mode.test.ts index 367a4cd0d..c0d7e3266 100644 --- a/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.enable.mode.test.ts +++ b/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.enable.mode.test.ts @@ -3,6 +3,7 @@ import { OWNABLE_VALIDATOR_ADDRESS, type Session, SmartSessionMode, + decodeSmartSessionSignature, encodeSmartSessionSignature, encodeValidationData, getEnableSessionDetails, @@ -21,8 +22,8 @@ import { createPublicClient, createWalletClient, encodeFunctionData, - getAddress, - encodePacked + encodePacked, + getAddress } from "viem" import { entryPoint07Address, @@ -40,8 +41,11 @@ import { type NexusClient, createSmartAccountClient } from "../../clients/createSmartAccountClient" +import { + MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS, + SMART_SESSIONS_ADDRESS +} from "../../constants" import { generateSalt } from "./Helpers" -import { MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS } from "../../constants" describe("modules.smartSessions.enable.mode.dx", async () => { let network: NetworkConfig @@ -109,7 +113,7 @@ describe("modules.smartSessions.enable.mode.dx", async () => { }) }) - test("should send a sponsored transaction", async () => { + test.skip("should send a sponsored transaction", async () => { // Get initial balance const initialBalance = await publicClient.getBalance({ address: nexusAccountAddress @@ -142,6 +146,51 @@ describe("modules.smartSessions.enable.mode.dx", async () => { expect(finalBalance).toBeLessThan(initialBalance) }) + test.skip("should support smart sessions use mode", async () => { + const uninitializedSmartSessions = getSmartSessionsValidator({}) + + const isInstalled = await nexusClient.isModuleInstalled({ + module: uninitializedSmartSessions + }) + + if (!isInstalled) { + const opHash = await nexusClient.installModule({ + module: uninitializedSmartSessions + }) + const installReceipt = await nexusClient.waitForUserOperationReceipt({ + hash: opHash + }) + expect(installReceipt.success).toBe("true") + } + + const session: Session = { + sessionValidator: OWNABLE_VALIDATOR_ADDRESS, + sessionValidatorInitData: encodeValidationData({ + threshold: 1, + owners: [sessionPublicKey] + }), + salt: generateSalt(), + userOpPolicies: [], + erc7739Policies: { + allowedERC7739Content: [], + erc1271Policies: [] + }, + actions: [ + { + actionTarget: testAddresses.Counter, + actionTargetSelector: "0x273ea3e3", // incrementNumber + actionPolicies: [getSudoPolicy()] + } + ], + chainId: BigInt(chain.id) + } + + const nexusAccount = getAccount({ + address: nexusClient.account.address, + type: "nexus" + }) + }) + test("should support smart sessions enable mode", async () => { const uninitializedSmartSessions = getSmartSessionsValidator({}) @@ -186,34 +235,37 @@ describe("modules.smartSessions.enable.mode.dx", async () => { type: "nexus" }) - const sessionDetailsWitPermissionEnableHash = await getEnableSessionDetails( - { + const sessionDetailsWithPermissionEnableHash = + await getEnableSessionDetails({ enableMode: SmartSessionMode.UNSAFE_ENABLE, sessions: [session], account: nexusAccount, - clients: [publicClient] - } - ) + clients: [publicClient], + enableValidatorAddress: MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS + }) const { permissionEnableHash, ...sessionDetails } = - sessionDetailsWitPermissionEnableHash + sessionDetailsWithPermissionEnableHash const sessionDetailKeys = Object.keys(sessionDetails) console.log({ sessionDetailKeys }) - const permissionEnableRawSig = await eoaAccount.signMessage({ - message: { raw: permissionEnableHash } - }) - sessionDetails.enableSessionData.enableSession.permissionEnableSig = - encodePacked( - ["address", "bytes"], - [MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS, permissionEnableRawSig] - ) + await eoaAccount.signMessage({ + message: { + raw: permissionEnableHash + } + }) + + // sessionDetails.enableSessionData.enableSession.permissionEnableSig = + // encodePacked( + // ["address", "bytes"], + // [MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS, permissionEnableRawSig] + // ) const nonce = await nexusClient.account.getNonce({ // @ts-ignore - moduleAddress: OWNABLE_VALIDATOR_ADDRESS + moduleAddress: SMART_SESSIONS_ADDRESS }) console.log({ nonce }) @@ -252,18 +304,12 @@ describe("modules.smartSessions.enable.mode.dx", async () => { }) const userOperation = await nexusClient.prepareUserOperation({ - account: nexusClient.account, calls, nonce, signature: encodeSmartSessionSignature(sessionDetails) }) - const userOpHashToSign = getUserOperationHash({ - chainId: chain.id, - entryPointAddress: entryPoint07Address, - entryPointVersion: "0.7", - userOperation - }) + const userOpHashToSign = nexusClient.account.getUserOpHash(userOperation) sessionDetails.signature = await sessionKeyAccount.signMessage({ message: { raw: userOpHashToSign } diff --git a/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.local.enable.mode.test.ts b/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.local.enable.mode.test.ts new file mode 100644 index 000000000..46826c9e7 --- /dev/null +++ b/src/sdk/modules/smartSessionsValidator/toSmartSessionValidator.local.enable.mode.test.ts @@ -0,0 +1,235 @@ +import { + encodeSmartSessionSignature, + encodeValidationData, + getAccount, + getEnableSessionDetails, + getOwnableValidatorMockSignature, + getSmartSessionsValidator, + getSudoPolicy, + OWNABLE_VALIDATOR_ADDRESS, + type Session, + SmartSessionMode +} from "@rhinestone/module-sdk" +import { + http, + type Abi, + type AbiFunction, + type Address, + type Chain, + type Hex, + type LocalAccount, + type PublicClient, + encodeFunctionData, + getContract, + slice, + toFunctionSelector +} from "viem" +import { generatePrivateKey, privateKeyToAccount } from "viem/accounts" +import { afterAll, beforeAll, describe, expect, test } from "vitest" +import { MockCalleeAbi } from "../../../test/__contracts/abi/MockCalleeAbi" +import { testAddresses } from "../../../test/callDatas" +import { toNetwork } from "../../../test/testSetup" +import { + fundAndDeployClients, + getTestAccount, + killNetwork, + toTestClient +} from "../../../test/testUtils" +import type { MasterClient, NetworkConfig } from "../../../test/testUtils" +import { + type NexusClient, + createSmartAccountClient +} from "../../clients/createSmartAccountClient" +import { SMART_SESSIONS_ADDRESS } from "../../constants" +import type { Module } from "../utils/Types" +import { generateSalt } from "./Helpers" +import { toSmartSessionsValidator } from "./toSmartSessionsValidator" +import { CounterAbi } from "../../../test/__contracts/abi/CounterAbi" + +describe.skip("modules.smartSessions.enable.mode.local.dx", async () => { + let network: NetworkConfig + let chain: Chain + let bundlerUrl: string + + // Test utils + let testClient: MasterClient + let eoaAccount: LocalAccount + let nexusClient: NexusClient + let nexusAccountAddress: Address + let sessionKeyAccount: LocalAccount + let sessionPublicKey: Address + let cachedSessionData: string + + let sessionsModule: Module + + beforeAll(async () => { + network = await toNetwork("BESPOKE_ANVIL_NETWORK_FORKING_BASE_SEPOLIA") + + chain = network.chain + bundlerUrl = network.bundlerUrl + eoaAccount = getTestAccount(0) + sessionKeyAccount = privateKeyToAccount(generatePrivateKey()) // Generally belongs to the dapp + sessionPublicKey = sessionKeyAccount.address + + testClient = toTestClient(chain, getTestAccount(5)) + + nexusClient = await createSmartAccountClient({ + signer: eoaAccount, + chain, + transport: http(), + bundlerTransport: http(bundlerUrl) + }) + + nexusAccountAddress = await nexusClient.account.getCounterFactualAddress() + + sessionsModule = toSmartSessionsValidator({ + account: nexusClient.account, + signer: eoaAccount + }) + + await fundAndDeployClients(testClient, [nexusClient]) + }) + + afterAll(async () => { + await killNetwork([network?.rpcPort, network?.bundlerPort]) + }) + + test("should check smart sessions enable mode on anvil network", async () => { + const uninitializedSmartSessions = getSmartSessionsValidator({}) + + const isInstalled = await nexusClient.isModuleInstalled({ + module: uninitializedSmartSessions + }) + + if (!isInstalled) { + const opHash = await nexusClient.installModule({ + module: uninitializedSmartSessions + }) + const installReceipt = await nexusClient.waitForUserOperationReceipt({ + hash: opHash + }) + expect(installReceipt.success).toBe(true) + } + + const session: Session = { + sessionValidator: OWNABLE_VALIDATOR_ADDRESS, + sessionValidatorInitData: encodeValidationData({ + threshold: 1, + owners: [sessionPublicKey] + }), + salt: generateSalt(), + userOpPolicies: [], + erc7739Policies: { + allowedERC7739Content: [], + erc1271Policies: [] + }, + actions: [ + { + actionTarget: testAddresses.Counter, + actionTargetSelector: "0x273ea3e3", // incrementNumber + actionPolicies: [getSudoPolicy()] + } + ], + chainId: BigInt(chain.id) + } + + const nexusAccount = getAccount({ + address: nexusClient.account.address, + type: "nexus" + }) + + const sessionDetailsWitPermissionEnableHash = await getEnableSessionDetails( + { + enableMode: SmartSessionMode.UNSAFE_ENABLE, + sessions: [session], + account: nexusAccount, + clients: [testClient as any] + // enableValidatorAddress: MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS + } + ) + + const { permissionEnableHash, ...sessionDetails } = + sessionDetailsWitPermissionEnableHash + + const sessionDetailKeys = Object.keys(sessionDetails) + console.log({ sessionDetailKeys }) + + sessionDetails.enableSessionData.enableSession.permissionEnableSig = + await eoaAccount.signMessage({ + message: { + raw: permissionEnableHash + } + }) + + // sessionDetails.enableSessionData.enableSession.permissionEnableSig = + // encodePacked( + // ["address", "bytes"], + // [MAINNET_ADDRESS_K1_VALIDATOR_ADDRESS, permissionEnableRawSig] + // ) + + const nonce = await nexusClient.account.getNonce({ + // @ts-ignore + moduleAddress: SMART_SESSIONS_ADDRESS + }) + + console.log({ nonce }) + + // const signature = encodeSmartSessionSignature(sessionDetails) + + // const decodededSignature = decodeSmartSessionSignature({ + // signature, + // account: nexusAccount + // }) + + // expect(decodededSignature.mode).toBe(SmartSessionMode.UNSAFE_ENABLE) + // expect(decodededSignature.permissionId).toBe(sessionDetails.permissionId) + // expect(decodededSignature.signature).toBe(sessionDetails.signature) + + // console.log({ decodededSignature }) + + const calls = [ + { + to: session.actions[0].actionTarget, + data: session.actions[0].actionTargetSelector + } + ] + + expect(session.actions[0].actionTargetSelector).toBe( + encodeFunctionData({ + abi: CounterAbi, + functionName: "incrementNumber" + }) + ) + + console.log({ calls }) + + sessionDetails.signature = getOwnableValidatorMockSignature({ + threshold: 1 + }) + + const userOperation = await nexusClient.prepareUserOperation({ + calls, + nonce, + signature: encodeSmartSessionSignature(sessionDetails) + }) + + const userOpHashToSign = nexusClient.account.getUserOpHash(userOperation) + + sessionDetails.signature = await sessionKeyAccount.signMessage({ + message: { raw: userOpHashToSign } + }) + + userOperation.signature = encodeSmartSessionSignature(sessionDetails) + + console.log({ userOperation }) + console.log("It fails at this point...") + + const userOpHash = await nexusClient.sendUserOperation(userOperation) + + const receipt = await nexusClient.waitForUserOperationReceipt({ + hash: userOpHash + }) + + console.log({ receipt }) + }) +})