diff --git a/.vitest/vitest.shared.ts b/.vitest/vitest.shared.ts index 50c85b8d64..2e2e1feb99 100644 --- a/.vitest/vitest.shared.ts +++ b/.vitest/vitest.shared.ts @@ -1,9 +1,11 @@ import { join } from "node:path"; import { configDefaults, defineConfig } from "vitest/config"; - +const typechecking = process.env["TYPECHECK"] === "true"; export const sharedConfig = defineConfig({ test: { typecheck: { + enabled: typechecking, + only: typechecking, ignoreSourceErrors: true, }, alias: { diff --git a/account-kit/infra/src/alchemyTransport.ts b/account-kit/infra/src/alchemyTransport.ts index cf3cf94d67..a48196bf59 100644 --- a/account-kit/infra/src/alchemyTransport.ts +++ b/account-kit/infra/src/alchemyTransport.ts @@ -8,6 +8,7 @@ import { import { createTransport, http, + type Chain, type EIP1193RequestFn, type HttpTransportConfig, type PublicRpcSchema, @@ -65,6 +66,22 @@ export type AlchemyTransport = AlchemyTransportBase & { config: AlchemyTransportConfig; }; +/** + * A type guard for the transport to determine if it is an Alchemy transport. + * Used in cases where we would like to do switching depending on the transport, where there used + * to be two clients for a alchemy and a non alchemy, and with this switch we don't need the two seperate clients. * + * + * @param {Transport} transport The transport to check + * @param {Chain} chain Chain for the transport to run its function to return the transport config + * @returns {boolean} `true` if the transport is an Alchemy transport, otherwise `false` + */ +export function isAlchemyTransport( + transport: Transport, + chain: Chain +): transport is AlchemyTransport { + return transport({ chain }).config.type === "alchemy"; +} + /** * Creates an Alchemy transport with the specified configuration options. * When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. diff --git a/account-kit/infra/src/index.ts b/account-kit/infra/src/index.ts index d0f4b43ae8..264971748d 100644 --- a/account-kit/infra/src/index.ts +++ b/account-kit/infra/src/index.ts @@ -2,7 +2,7 @@ export type * from "./actions/simulateUserOperationChanges.js"; export { simulateUserOperationChanges } from "./actions/simulateUserOperationChanges.js"; export type * from "./actions/types.js"; export type * from "./alchemyTransport.js"; -export { alchemy } from "./alchemyTransport.js"; +export { alchemy, isAlchemyTransport } from "./alchemyTransport.js"; export type * from "./chains.js"; export { arbitrum, diff --git a/account-kit/smart-contracts/src/light-account/clients/alchemyClient.test.ts b/account-kit/smart-contracts/src/light-account/clients/alchemyClient.test.ts index 22d6247169..b28be01efa 100644 --- a/account-kit/smart-contracts/src/light-account/clients/alchemyClient.test.ts +++ b/account-kit/smart-contracts/src/light-account/clients/alchemyClient.test.ts @@ -13,6 +13,7 @@ import { import { Alchemy, Network } from "alchemy-sdk"; import { avalanche, type Chain } from "viem/chains"; import { createLightAccountAlchemyClient } from "./alchemyClient.js"; +import { createLightAccountClient } from "./client.js"; describe("Light Account Client Tests", () => { const dummyMnemonic = @@ -136,7 +137,7 @@ describe("Light Account Client Tests", () => { signer: SmartAccountSigner; chain: Chain; }) => - createLightAccountAlchemyClient({ + createLightAccountClient({ transport: alchemy({ jwt: "test", }), diff --git a/account-kit/smart-contracts/src/light-account/clients/alchemyClient.ts b/account-kit/smart-contracts/src/light-account/clients/alchemyClient.ts index 12b7b8a19f..b94af6f5e8 100644 --- a/account-kit/smart-contracts/src/light-account/clients/alchemyClient.ts +++ b/account-kit/smart-contracts/src/light-account/clients/alchemyClient.ts @@ -1,12 +1,10 @@ import type { HttpTransport, SmartAccountSigner } from "@aa-sdk/core"; import { - createAlchemySmartAccountClient, type AlchemySmartAccountClient, type AlchemySmartAccountClientConfig, } from "@account-kit/infra"; import { - createLightAccount, - lightAccountClientActions, + createLightAccountClient, type CreateLightAccountParams, type LightAccount, type LightAccountClientActions, @@ -49,7 +47,7 @@ export async function createLightAccountAlchemyClient< * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()) * }); * ``` - * + * @deprecated Use createLightAccountClient instead now, it should switch depending on the transport * @param {AlchemyLightAccountClientConfig} config The configuration for setting up the Alchemy Light Account Client * @returns {Promise} A promise that resolves to an `AlchemySmartAccountClient` object containing the created client */ @@ -59,17 +57,10 @@ export async function createLightAccountAlchemyClient({ chain, ...config }: AlchemyLightAccountClientConfig): Promise { - const account = await createLightAccount({ - ...config, + return createLightAccountClient({ + opts, transport, chain, - }); - - return createAlchemySmartAccountClient({ ...config, - transport, - chain, - account, - opts, - }).extend(lightAccountClientActions); + }); } diff --git a/account-kit/smart-contracts/src/light-account/clients/client.ts b/account-kit/smart-contracts/src/light-account/clients/client.ts index b700030efc..af83979ab2 100644 --- a/account-kit/smart-contracts/src/light-account/clients/client.ts +++ b/account-kit/smart-contracts/src/light-account/clients/client.ts @@ -11,19 +11,18 @@ import { createLightAccount, type CreateLightAccountParams, type LightAccount, -} from "../accounts/account.js"; +} from "@account-kit/smart-contracts"; import { lightAccountClientActions, type LightAccountClientActions, } from "../decorators/lightAccount.js"; import { + isAlchemyTransport, + createAlchemySmartAccountClient, type AlchemySmartAccountClient, type AlchemyTransport, } from "@account-kit/infra"; -import { - createLightAccountAlchemyClient, - type AlchemyLightAccountClientConfig, -} from "./alchemyClient.js"; +import { type AlchemyLightAccountClientConfig } from "./alchemyClient.js"; export type CreateLightAccountClientParams< TTransport extends Transport | AlchemyTransport = Transport, @@ -85,6 +84,19 @@ export function createLightAccountClient< * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()) * }); * ``` + * @example + * ```ts + * import { createLightAccountClient } from "@account-kit/smart-contracts"; + * import { sepolia, alchemy } from "@account-kit/infra"; + * import { LocalAccountSigner } from "@aa-sdk/core"; + * import { generatePrivateKey } from "viem" + * + * const lightAlchemyAccountClient = await createLightAccountClient({ + * transport: alchemy({ apiKey: "your-api-key" }), + * chain: sepolia, + * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()) + * }); + * ``` * * @param {CreateLightAccountClientParams} params The parameters for creating a light account client * @returns {Promise} A promise that resolves to a `SmartAccountClient` object containing the created account information and methods @@ -94,18 +106,19 @@ export async function createLightAccountClient( ): Promise { const { transport, chain } = params; - if (isAlchemyTransport(transport, chain)) { - return await createLightAccountAlchemyClient({ - ...params, - transport, - }); - } - const lightAccount = await createLightAccount({ ...params, transport, chain, }); + if (isAlchemyTransport(transport, chain)) { + return createAlchemySmartAccountClient({ + ...params, + transport, + chain, + account: lightAccount, + }).extend(lightAccountClientActions); + } return createSmartAccountClient({ ...params, @@ -114,10 +127,3 @@ export async function createLightAccountClient( account: lightAccount, }).extend(lightAccountClientActions); } - -function isAlchemyTransport( - transport: Transport, - chain: Chain -): transport is AlchemyTransport { - return transport({ chain }).config.type === "alchemy"; -} diff --git a/account-kit/smart-contracts/src/light-account/clients/multiOwnerAlchemyClient.test.ts b/account-kit/smart-contracts/src/light-account/clients/multiOwnerAlchemyClient.test.ts index 4faff104ab..93ca19dda4 100644 --- a/account-kit/smart-contracts/src/light-account/clients/multiOwnerAlchemyClient.test.ts +++ b/account-kit/smart-contracts/src/light-account/clients/multiOwnerAlchemyClient.test.ts @@ -13,6 +13,7 @@ import { import { Alchemy, Network } from "alchemy-sdk"; import { avalanche, type Chain } from "viem/chains"; import { createMultiOwnerLightAccountAlchemyClient } from "./multiOwnerAlchemyClient.js"; +import { createMultiOwnerLightAccountClient } from "./multiOwnerLightAccount.js"; describe("MultiOwnerLightAccount Client Tests", () => { const dummyMnemonic = @@ -138,7 +139,7 @@ describe("MultiOwnerLightAccount Client Tests", () => { signer: SmartAccountSigner; chain: Chain; }) => - createMultiOwnerLightAccountAlchemyClient({ + createMultiOwnerLightAccountClient({ transport: alchemy({ jwt: "test", }), diff --git a/account-kit/smart-contracts/src/light-account/clients/multiOwnerAlchemyClient.ts b/account-kit/smart-contracts/src/light-account/clients/multiOwnerAlchemyClient.ts index 86c11ec7f0..05477bfb86 100644 --- a/account-kit/smart-contracts/src/light-account/clients/multiOwnerAlchemyClient.ts +++ b/account-kit/smart-contracts/src/light-account/clients/multiOwnerAlchemyClient.ts @@ -1,12 +1,10 @@ import type { HttpTransport, SmartAccountSigner } from "@aa-sdk/core"; import { - createAlchemySmartAccountClient, type AlchemySmartAccountClient, type AlchemySmartAccountClientConfig, } from "@account-kit/infra"; import { - createMultiOwnerLightAccount, - multiOwnerLightAccountClientActions, + createMultiOwnerLightAccountClient, type CreateMultiOwnerLightAccountParams, type MultiOwnerLightAccount, type MultiOwnerLightAccountClientActions, @@ -55,6 +53,7 @@ export async function createMultiOwnerLightAccountAlchemyClient< * }); * ``` * + * @deprecated Use createMultiOwnerLightAccountAlchemyClient instead now, it should switch depending on the transport * @param {AlchemyMultiOwnerLightAccountClientConfig} config The configuration for creating the Alchemy client * @returns {Promise} A promise that resolves to an `AlchemySmartAccountClient` object containing the created account information and methods */ @@ -64,17 +63,10 @@ export async function createMultiOwnerLightAccountAlchemyClient({ chain, ...config }: AlchemyMultiOwnerLightAccountClientConfig): Promise { - const account = await createMultiOwnerLightAccount({ - ...config, + return createMultiOwnerLightAccountClient({ + opts, transport, chain, - }); - - return createAlchemySmartAccountClient({ ...config, - transport, - chain, - account, - opts, - }).extend(multiOwnerLightAccountClientActions); + }); } diff --git a/account-kit/smart-contracts/src/light-account/clients/multiOwnerLightAccount.test-d.ts b/account-kit/smart-contracts/src/light-account/clients/multiOwnerLightAccount.test-d.ts new file mode 100644 index 0000000000..c4219892d2 --- /dev/null +++ b/account-kit/smart-contracts/src/light-account/clients/multiOwnerLightAccount.test-d.ts @@ -0,0 +1,141 @@ +import { + createBundlerClient, + createSmartAccountClientFromExisting, + erc7677Middleware, + LocalAccountSigner, + type Address, + type SmartAccountSigner, +} from "@aa-sdk/core"; +import { custom, type Chain } from "viem"; +import { generatePrivateKey } from "viem/accounts"; +import { setBalance } from "viem/actions"; +import { accounts } from "~test/constants.js"; +import { local070Instance } from "~test/instances.js"; +import { multiOwnerPluginActions } from "../../msca/plugins/multi-owner/index.js"; +import { getMSCAUpgradeToData } from "../../msca/utils.js"; +import type { LightAccountVersion } from "../types"; +import { createMultiOwnerLightAccountClient } from "./multiOwnerLightAccount.js"; +import { + alchemy, + alchemyEnhancedApiActions, + arbitrumSepolia, +} from "@account-kit/infra"; +import { Alchemy, Network } from "alchemy-sdk"; + +describe("Types: MultiOwner Light Account Tests", () => { + const instance = local070Instance; + let client: ReturnType; + + beforeAll(async () => { + client = instance.getClient(); + }); + + const signer: SmartAccountSigner = new LocalAccountSigner( + accounts.fundedAccountOwner + ); + + it("should upgrade a deployed multi owner light account to msca successfully", async () => { + // create a owner signer to create the account + const throwawaySigner = LocalAccountSigner.privateKeyToAccountSigner( + generatePrivateKey() + ); + const throwawayClient = await givenConnectedProvider({ + signer: throwawaySigner, + }); + + const accountAddress = throwawayClient.getAddress(); + const ownerAddress = await throwawaySigner.getAddress(); + + // fund + deploy the throwaway address + await setBalance(client, { + address: accountAddress, + value: 200000000000000000n, + }); + + const { createMAAccount, ...upgradeToData } = await getMSCAUpgradeToData( + throwawayClient, + { + account: throwawayClient.account, + multiOwnerPluginAddress: "0xcE0000007B008F50d762D155002600004cD6c647", + } + ); + + await throwawayClient.upgradeAccount({ + upgradeTo: upgradeToData, + waitForTx: true, + }); + + const upgradedClient = createSmartAccountClientFromExisting({ + client: createBundlerClient({ + chain: instance.chain, + transport: custom(client), + }), + account: await createMAAccount(), + }).extend(multiOwnerPluginActions); + + const upgradedAccountAddress = upgradedClient.getAddress(); + + const owners = await upgradedClient.readOwners({ + account: upgradedClient.account, + pluginAddress: "0xcE0000007B008F50d762D155002600004cD6c647", + }); + + expect(upgradedAccountAddress).toBe(accountAddress); + expect(owners).toContain(ownerAddress); + }, 200000); + + it("should have enhanced api properties on the provider", async () => { + const chain = arbitrumSepolia; + const alchemy = new Alchemy({ + network: Network.MATIC_MUMBAI, + apiKey: "test", + }); + + const provider = ( + await givenAlchemyConnectedProvider({ signer, chain }) + ).extend(alchemyEnhancedApiActions(alchemy)); + + expect(provider.account).toBeDefined(); + expect(provider.waitForUserOperationTransaction).toBeDefined(); + expect(provider.sendUserOperation).toBeDefined(); + expect(provider.core).toBeDefined(); + }); + const givenAlchemyConnectedProvider = async ({ + signer, + chain, + }: { + signer: SmartAccountSigner; + chain: Chain; + }) => + createMultiOwnerLightAccountClient({ + transport: alchemy({ + jwt: "test", + }), + chain, + signer, + accountAddress: "0x86f3B0211764971Ad0Fc8C8898d31f5d792faD84", + }); + + const givenConnectedProvider = ({ + signer, + version = "v2.0.0", + accountAddress, + usePaymaster = false, + accountIndex, + }: { + signer: SmartAccountSigner; + version?: LightAccountVersion<"MultiOwnerLightAccount">; + usePaymaster?: boolean; + accountAddress?: Address; + accountIndex?: bigint; + }) => + createMultiOwnerLightAccountClient({ + signer, + accountAddress, + version, + transport: custom(client), + chain: instance.chain, + salt: accountIndex, + ...(usePaymaster ? erc7677Middleware() : {}), + }); +}); diff --git a/account-kit/smart-contracts/src/light-account/clients/multiOwnerLightAccount.ts b/account-kit/smart-contracts/src/light-account/clients/multiOwnerLightAccount.ts index d6004852b6..1da98a1597 100644 --- a/account-kit/smart-contracts/src/light-account/clients/multiOwnerLightAccount.ts +++ b/account-kit/smart-contracts/src/light-account/clients/multiOwnerLightAccount.ts @@ -8,14 +8,19 @@ import { } from "@aa-sdk/core"; import { type Chain, type CustomTransport, type Transport } from "viem"; import { + multiOwnerLightAccountClientActions, createMultiOwnerLightAccount, type CreateMultiOwnerLightAccountParams, type MultiOwnerLightAccount, -} from "../accounts/multiOwner.js"; -import { - multiOwnerLightAccountClientActions, type MultiOwnerLightAccountClientActions, -} from "../decorators/multiOwnerLightAccount.js"; + type AlchemyMultiOwnerLightAccountClientConfig, +} from "@account-kit/smart-contracts"; +import { + isAlchemyTransport, + createAlchemySmartAccountClient, + type AlchemySmartAccountClient, + type AlchemyTransport, +} from "@account-kit/infra"; export type CreateMultiOwnerLightAccountClientParams< TTransport extends Transport = Transport, @@ -36,6 +41,30 @@ export type CreateMultiOwnerLightAccountClientParams< "transport" | "account" | "chain" >; +export type CreateMultiOwnerLightAccountClientDynamicTransportParams< + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TSigner extends SmartAccountSigner = SmartAccountSigner +> = + | (AlchemyMultiOwnerLightAccountClientConfig & { + transport: AlchemyTransport; + }) + | CreateMultiOwnerLightAccountClientParams; + +export async function createMultiOwnerLightAccountClient< + TSigner extends SmartAccountSigner = SmartAccountSigner +>( + params: AlchemyMultiOwnerLightAccountClientConfig & { + transport: AlchemyTransport; + } +): Promise< + AlchemySmartAccountClient< + Chain | undefined, + MultiOwnerLightAccount, + MultiOwnerLightAccountClientActions + > +>; + export function createMultiOwnerLightAccountClient< TChain extends Chain | undefined = Chain | undefined, TSigner extends SmartAccountSigner = SmartAccountSigner @@ -71,12 +100,28 @@ export function createMultiOwnerLightAccountClient< * }); * ``` * - * @param {CreateMultiOwnerLightAccountClientParams} params the configuration for creating the multi-owner light account client + * @example + * ```ts + * import { createMultiOwnerLightAccountClient } from "@account-kit/smart-contracts"; + * import { sepolia, alchemy } from "@account-kit/infra"; + * import { LocalAccountSigner } from "@aa-sdk/core"; + * import { generatePrivateKey } from "viem" + * + * const lightAccountClient = await createMultiOwnerLightAccountClient({ + * transport: alchemy({ + * apiKey: "your-api-key", + * }), + * chain: sepolia + * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()) + * }); + * ``` + * + * @param {CreateMultiOwnerLightAccountClientDynamicTransportParams} params the configuration for creating the multi-owner light / alchemy account client with the provided parameters transport * @returns {Promise} a promise that resolves to a `SmartAccountClient` containing the created account client and relevant methods */ export async function createMultiOwnerLightAccountClient( - params: CreateMultiOwnerLightAccountClientParams -): Promise { + params: CreateMultiOwnerLightAccountClientDynamicTransportParams +): Promise { const { transport, chain } = params; const lightAccount = await createMultiOwnerLightAccount({ @@ -84,6 +129,14 @@ export async function createMultiOwnerLightAccountClient( transport, chain, }); + if (isAlchemyTransport(transport, chain)) { + return createAlchemySmartAccountClient({ + ...params, + transport, + chain, + account: lightAccount, + }).extend(multiOwnerLightAccountClientActions); + } return createSmartAccountClient({ ...params, diff --git a/account-kit/smart-contracts/src/msca/client/alchemyClient.ts b/account-kit/smart-contracts/src/msca/client/alchemyClient.ts index ef47b8b6a7..20e37eadf4 100644 --- a/account-kit/smart-contracts/src/msca/client/alchemyClient.ts +++ b/account-kit/smart-contracts/src/msca/client/alchemyClient.ts @@ -1,14 +1,10 @@ import type { SmartAccountSigner } from "@aa-sdk/core"; import { - createAlchemySmartAccountClient, type AlchemySmartAccountClient, type AlchemySmartAccountClientConfig, } from "@account-kit/infra"; import { - accountLoupeActions, - createMultiOwnerModularAccount, - multiOwnerPluginActions, - pluginManagerActions, + createMultiOwnerModularAccountClient, type AccountLoupeActions, type CreateMultiOwnerModularAccountParams, type LightAccount, @@ -59,27 +55,12 @@ export function createModularAccountAlchemyClient< * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()) * }); * ``` - * + * @deprecated Use createModularAccountClient instead of this function, we are switching based on the transport * @param {AlchemyModularAccountClientConfig} config The configuration for creating the Alchemy client * @returns {Promise} A promise that resolves to an `AlchemySmartAccountClient` configured with the desired plugins and actions */ export async function createModularAccountAlchemyClient( config: AlchemyModularAccountClientConfig ): Promise { - const { transport, chain, opts } = config; - - const account = await createMultiOwnerModularAccount({ - ...config, - transport, - chain, - }); - - return createAlchemySmartAccountClient({ - ...config, - account, - opts, - }) - .extend(multiOwnerPluginActions) - .extend(pluginManagerActions) - .extend(accountLoupeActions); + return createMultiOwnerModularAccountClient(config); } diff --git a/account-kit/smart-contracts/src/msca/client/client.ts b/account-kit/smart-contracts/src/msca/client/client.ts index b5be02561f..65e61d01b9 100644 --- a/account-kit/smart-contracts/src/msca/client/client.ts +++ b/account-kit/smart-contracts/src/msca/client/client.ts @@ -35,8 +35,16 @@ import { type MultisigUserOperationContext, } from "../plugins/multisig/index.js"; import { multisigSignatureMiddleware } from "../plugins/multisig/middleware.js"; +import type { AlchemyModularAccountClientConfig } from "./alchemyClient.js"; +import { + createAlchemySmartAccountClient, + isAlchemyTransport, + type AlchemySmartAccountClient, + type AlchemyTransport, +} from "@account-kit/infra"; +import type { AlchemyMultisigAccountClientConfig } from "./multiSigAlchemyClient.js"; -export type CreateMultiOwnerModularAccountClientParams< +export type CreateMultiOwnerModularAccountClientWithoutAlchemyParams< TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, TSigner extends SmartAccountSigner = SmartAccountSigner @@ -45,8 +53,19 @@ export type CreateMultiOwnerModularAccountClientParams< "transport" | "chain" > & Omit, "account">; +export type CreateMultiOwnerModularAccountClientParams< + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TSigner extends SmartAccountSigner = SmartAccountSigner +> = + | CreateMultiOwnerModularAccountClientWithoutAlchemyParams< + TTransport, + TChain, + TSigner + > + | AlchemyModularAccountClientConfig; -export type CreateMultisigModularAccountClientParams< +export type CreateMultisigModularAccountClientWithoutAlchemyParams< TTransport extends Transport = Transport, TChain extends Chain | undefined = Chain | undefined, TSigner extends SmartAccountSigner = SmartAccountSigner @@ -56,6 +75,34 @@ export type CreateMultisigModularAccountClientParams< > & Omit, "account">; +export type CreateMultisigModularAccountClientParams< + TTransport extends Transport = Transport, + TChain extends Chain | undefined = Chain | undefined, + TSigner extends SmartAccountSigner = SmartAccountSigner +> = + | CreateMultisigModularAccountClientWithoutAlchemyParams< + TTransport, + TChain, + TSigner + > + | AlchemyMultisigAccountClientConfig; + +export function createMultiOwnerModularAccountClient< + TSigner extends SmartAccountSigner = SmartAccountSigner +>( + params: AlchemyModularAccountClientConfig & { + transport: AlchemyTransport; + } +): Promise< + AlchemySmartAccountClient< + Chain | undefined, + MultiOwnerModularAccount, + MultiOwnerPluginActions> & + PluginManagerActions> & + AccountLoupeActions> + > +>; + export function createMultiOwnerModularAccountClient< TChain extends Chain | undefined = Chain | undefined, TSigner extends SmartAccountSigner = SmartAccountSigner @@ -89,6 +136,19 @@ export function createMultiOwnerModularAccountClient< * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()) * }); * ``` + * @example + * ```ts + * import { createMultiOwnerModularAccountClient } from "@account-kit/smart-contracts"; + * import { sepolia, alchemy } from "@account-kit/infra"; + * import { LocalAccountSigner } from "@aa-sdk/core"; + * import { generatePrivateKey } from "viem" + * + * const alchemyAccountClient = await createMultiOwnerModularAccountClient({ + * transport: alchemy({ apiKey: "your-api-key" }), + * chain: sepolia, + * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()) + * }); + * ``` * * @param {CreateMultiOwnerModularAccountClientParams} config The parameters for creating the multi-owner modular account client * @returns {Promise} A promise that resolves to a `SmartAccountClient` instance with extended plugin actions @@ -97,12 +157,28 @@ export async function createMultiOwnerModularAccountClient({ transport, chain, ...params -}: CreateMultiOwnerModularAccountClientParams): Promise { +}: CreateMultiOwnerModularAccountClientParams): Promise< + SmartAccountClient | AlchemySmartAccountClient +> { const modularAccount = await createMultiOwnerModularAccount({ ...params, transport, chain, }); + if (isAlchemyTransport(transport, chain)) { + const { opts } = params; + + return createAlchemySmartAccountClient({ + ...params, + account: modularAccount, + transport, + chain, + opts, + }) + .extend(multiOwnerPluginActions) + .extend(pluginManagerActions) + .extend(accountLoupeActions); + } return createSmartAccountClient({ ...params, @@ -115,6 +191,21 @@ export async function createMultiOwnerModularAccountClient({ .extend(accountLoupeActions); } +export function createMultisigModularAccountClient< + TSigner extends SmartAccountSigner = SmartAccountSigner +>( + params: AlchemyMultisigAccountClientConfig +): Promise< + AlchemySmartAccountClient< + Chain | undefined, + MultisigModularAccount, + MultisigPluginActions> & + PluginManagerActions> & + AccountLoupeActions>, + MultisigUserOperationContext + > +>; + export function createMultisigModularAccountClient< TChain extends Chain | undefined = Chain | undefined, TSigner extends SmartAccountSigner = SmartAccountSigner @@ -152,6 +243,21 @@ export function createMultisigModularAccountClient< * threshold: 2, // 2 of N signatures * }); * ``` + * @example + * ```ts + * import { createMultisigModularAccountClient } from "@account-kit/smart-contracts"; + * import { sepolia } from "@account-kit/infra"; + * import { LocalAccountSigner } from "@aa-sdk/core"; + * import { generatePrivateKey } from "viem" + * + * const alchemyAccountClient = await createMultisigModularAccountClient({ + * transport: alchemy({ apiKey: "your-api-key" }), + * chain: sepolia, + * signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()), + * owners: [...], // other owners on the account + * threshold: 2, // 2 of N signatures + * }); + * ``` * * @param {CreateMultisigModularAccountClientParams} config the parameters for configuring the multisig modular account client * @returns {Promise, {}, SmartAccountClientRpcSchema, MultisigUserOperationContext>>} a promise that resolves to a `SmartAccountClient` object extended with the multisig modular account and additional actions @@ -161,20 +267,49 @@ export async function createMultisigModularAccountClient({ chain, ...params }: CreateMultisigModularAccountClientParams): Promise< - SmartAccountClient< - Transport, - Chain, - MultisigModularAccount, - {}, - SmartAccountClientRpcSchema, - MultisigUserOperationContext - > + | SmartAccountClient< + Transport, + Chain, + MultisigModularAccount, + {}, + SmartAccountClientRpcSchema, + MultisigUserOperationContext + > + | AlchemySmartAccountClient< + Chain | undefined, + MultisigModularAccount, + MultisigPluginActions> & + PluginManagerActions> & + AccountLoupeActions>, + MultisigUserOperationContext + > > { const modularAccount = await createMultisigModularAccount({ ...params, transport, chain, }); + if (isAlchemyTransport(transport, chain)) { + // Need to fit the type into this since the previous multiSigAlchemyClient had it at this point, but without an Value as Type should be safe + // And the createAlchemySmartAccountClient signUserOperation could not infer without this + let config: AlchemyMultisigAccountClientConfig = { + ...params, + chain, + transport, + }; + const { opts } = config; + + return createAlchemySmartAccountClient({ + ...config, + account: modularAccount, + opts, + signUserOperation: multisigSignatureMiddleware, + }) + .extend(smartAccountClientActions) + .extend(multisigPluginActions) + .extend(pluginManagerActions) + .extend(accountLoupeActions); + } const client = createSmartAccountClient({ ...params, diff --git a/account-kit/smart-contracts/src/msca/client/multiSigAlchemyClient.ts b/account-kit/smart-contracts/src/msca/client/multiSigAlchemyClient.ts index 96c3cfb465..1914ad2c17 100644 --- a/account-kit/smart-contracts/src/msca/client/multiSigAlchemyClient.ts +++ b/account-kit/smart-contracts/src/msca/client/multiSigAlchemyClient.ts @@ -1,18 +1,11 @@ +import { type SmartAccountSigner } from "@aa-sdk/core"; import { - smartAccountClientActions, - type SmartAccountSigner, -} from "@aa-sdk/core"; -import { - createAlchemySmartAccountClient, type AlchemySmartAccountClient, type AlchemySmartAccountClientConfig, + type AlchemyTransport, } from "@account-kit/infra"; import { - accountLoupeActions, - createMultisigModularAccount, - multisigPluginActions, - multisigSignatureMiddleware, - pluginManagerActions, + createMultisigModularAccountClient, type AccountLoupeActions, type CreateMultisigModularAccountParams, type LightAccount, @@ -38,7 +31,7 @@ export type AlchemyMultisigAccountClientConfig< MultisigUserOperationContext >, "account" - >; + > & { transport: AlchemyTransport }; export function createMultisigAccountAlchemyClient< TSigner extends SmartAccountSigner = SmartAccountSigner @@ -74,6 +67,7 @@ export function createMultisigAccountAlchemyClient< * }); * ``` * + * @deprecated Use createModularAccountClient instead of this function, we are switching based on the transport * @param {AlchemyMultisigAccountClientConfig} config The configuration for the Alchemy multisig account client * @returns {Promise, MultisigPluginActions> & PluginManagerActions> & AccountLoupeActions>, MultisigUserOperationContext>>} A promise that resolves to an Alchemy Smart Account Client for multisig accounts with extended functionalities. */ @@ -89,22 +83,5 @@ export async function createMultisigAccountAlchemyClient( MultisigUserOperationContext > > { - const { transport, opts, chain } = config; - - const account = await createMultisigModularAccount({ - ...config, - transport, - chain, - }); - - return createAlchemySmartAccountClient({ - ...config, - account, - opts, - signUserOperation: multisigSignatureMiddleware, - }) - .extend(smartAccountClientActions) - .extend(multisigPluginActions) - .extend(pluginManagerActions) - .extend(accountLoupeActions); + return createMultisigModularAccountClient(config); } diff --git a/package.json b/package.json index f2d077d767..6731d72f40 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "docs:gen": "turbo run docs:gen", "test": "vitest dev", "test:ci": "vitest run", - "test:typecheck": "TYPECHECK=true vitest --typecheck --typecheck.only ", + "test:typecheck": "TYPECHECK=true vitest --typecheck --typecheck.only run", "lint:write": "eslint . --fix && yarn docs:gen && prettier --loglevel warn --write --ignore-unknown .", "lint:check": "eslint . && prettier --check .", "prepare": "husky install && yarn turbo prepare", diff --git a/site/pages/reference/account-kit/infra/functions/isAlchemyTransport.mdx b/site/pages/reference/account-kit/infra/functions/isAlchemyTransport.mdx new file mode 100644 index 0000000000..3e60873e3c --- /dev/null +++ b/site/pages/reference/account-kit/infra/functions/isAlchemyTransport.mdx @@ -0,0 +1,34 @@ +--- +# This file is autogenerated +title: isAlchemyTransport +description: Overview of the isAlchemyTransport method +--- + +# isAlchemyTransport + +A type guard for the transport to determine if it is an Alchemy transport. +Used in cases where we would like to do switching depending on the transport, where there used +to be two clients for a alchemy and a non alchemy, and with this switch we don't need the two seperate clients. \* + +## Import + +```ts +import { isAlchemyTransport } from "@account-kit/infra"; +``` + +## Parameters + +### transport + +`Transport` +The transport to check + +### chain + +`Chain` +Chain for the transport to run its function to return the transport config + +## Returns + +`boolean` +`true` if the transport is an Alchemy transport, otherwise `false` diff --git a/site/pages/reference/account-kit/smart-contracts/functions/createMultiOwnerLightAccountClient.mdx b/site/pages/reference/account-kit/smart-contracts/functions/createMultiOwnerLightAccountClient.mdx index a04c44cda3..afdf2d1c40 100644 --- a/site/pages/reference/account-kit/smart-contracts/functions/createMultiOwnerLightAccountClient.mdx +++ b/site/pages/reference/account-kit/smart-contracts/functions/createMultiOwnerLightAccountClient.mdx @@ -34,8 +34,8 @@ const account = await createMultiOwnerLightAccountClient({ ### params -`CreateMultiOwnerLightAccountClientParams` -the configuration for creating the multi-owner light account client +`CreateMultiOwnerLightAccountClientDynamicTransportParams` +the configuration for creating the multi-owner light / alchemy account client with the provided parameters transport ## Returns diff --git a/site/pages/third-party/paymasters.mdx b/site/pages/third-party/paymasters.mdx index aa89d1e9d3..20617c704e 100644 --- a/site/pages/third-party/paymasters.mdx +++ b/site/pages/third-party/paymasters.mdx @@ -118,15 +118,12 @@ const stackupClient = createClient({ transport: http("https://api.stackup.sh/v1/paymaster/STACKUP_API_KEY"), }); -const alchemyTransport = alchemy({ - // TODO: Replace with your Alchemy API key (https://dashboard.alchemypreview.com/apps) - apiKey: "ALCHEMY_API_KEY", -}); +const alchemyTransport = http("ALCHEMY_RPC_URL"); const alchemyClient = await createMultiOwnerModularAccountClient({ chain, signer, - transport: alchemyTransport, + transport: http("ALCHEMY_RPC_URL"), // Bypasses alchemy gas estimation and instead uses Stackup for gas estimation gasEstimator: async (userOp) => ({ ...userOp, diff --git a/site/sidebar/reference/aa-sdk/core.ts b/site/sidebar/reference/aa-sdk/core.ts index 26adf0f0e7..dec87e6814 100644 --- a/site/sidebar/reference/aa-sdk/core.ts +++ b/site/sidebar/reference/aa-sdk/core.ts @@ -232,6 +232,15 @@ export const aaSdkCoreReferenceSidebar: SidebarItem[] = [ }, ], }, + { + text: "EntityIdOverrideError", + items: [ + { + text: "constructor", + link: "/reference/aa-sdk/core/classes/EntityIdOverrideError/constructor", + }, + ], + }, { text: "EntryPointNotFoundError", items: [ @@ -286,6 +295,15 @@ export const aaSdkCoreReferenceSidebar: SidebarItem[] = [ }, ], }, + { + text: "InvalidEntityIdError", + items: [ + { + text: "constructor", + link: "/reference/aa-sdk/core/classes/InvalidEntityIdError/constructor", + }, + ], + }, { text: "InvalidEntryPointError", items: [ @@ -295,6 +313,15 @@ export const aaSdkCoreReferenceSidebar: SidebarItem[] = [ }, ], }, + { + text: "InvalidNonceKeyError", + items: [ + { + text: "constructor", + link: "/reference/aa-sdk/core/classes/InvalidNonceKeyError/constructor", + }, + ], + }, { text: "InvalidRpcUrlError", items: [ diff --git a/site/sidebar/reference/account-kit/infra.ts b/site/sidebar/reference/account-kit/infra.ts index ebfae00503..cdee18d72f 100644 --- a/site/sidebar/reference/account-kit/infra.ts +++ b/site/sidebar/reference/account-kit/infra.ts @@ -62,6 +62,10 @@ export const accountKitInfraReferenceSidebar: SidebarItem[] = [ text: "isAlchemySmartAccountClient", link: "/reference/account-kit/infra/functions/isAlchemySmartAccountClient", }, + { + text: "isAlchemyTransport", + link: "/reference/account-kit/infra/functions/isAlchemyTransport", + }, { text: "simulateUserOperationChanges", link: "/reference/account-kit/infra/functions/simulateUserOperationChanges",