Skip to content

Upgrading to a Modular Account

Upgrading a SmartContractAccount can be done easily using Account Kit. It just involves a simple call to a single function on the SmartAccountClient, namely upgradeAccount, along with the necessary call data, UpgradeToData, for the account targeted for the upgrade. For upgrading to a Modular Account, you can use the utility function getMSCAUpgradeToData provided by the @account-kit/smart-contracts package to retrieve the call data for the upgrade. This process applies to any account with upgrade capabilities.

Using the Light Account as an example, here is an overview of how the upgrade can be executed using a Smart Account Client:

example.ts
// @filename: lightAccountClient.ts
import { createLightAccountAlchemyClient } from "@account-kit/smart-contracts";
import { sepolia } from "@account-kit/infra";
import { LocalAccountSigner } from "@aa-sdk/core";
import { generatePrivateKey } from "viem/accounts";
 
export const lightAccountClient = await createLightAccountAlchemyClient({
  apiKey: "YOUR_API_KEY",
  chain: sepolia,
  signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
});
// @filename: lightAccountClient.ts
import { createLightAccountAlchemyClient } from "@account-kit/smart-contracts";
import { sepolia } from "@account-kit/infra";
import { LocalAccountSigner } from "@aa-sdk/core";
import { generatePrivateKey } from "viem/accounts";
 
export const lightAccountClient = await createLightAccountAlchemyClient({
  apiKey: "YOUR_API_KEY",
  chain: sepolia,
  signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
});
// @filename: example.js
 
// ---cut---
import { lightAccountClient } from "./lightAccountClient";
import { getMSCAUpgradeToData } from "@account-kit/smart-contracts";
 
const { createMAAccount, ...upgradeToData } = await getMSCAUpgradeToData(
  lightAccountClient,
  { account: lightAccountClient.account }
);
 
const hash = await lightAccountClient.upgradeAccount({
  upgradeTo: upgradeToData,
  waitForTx: true,
});
 
const upgradedAccount = await createMAAccount();
## Errors were thrown in the sample, but not included in an error tag These errors were not marked as being expected: 2353. Expected: // @errors: 2353 Compiler Errors: lightAccountClient.ts [2353] 342 - Object literal may only specify known properties, and 'apiKey' does not exist in type 'AlchemyLightAccountClientConfig<LocalAccountSigner<{ address: `0x${string}`; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: `0x${string}`; }) => Promise<`0x${string}`>; experimental_signAuthorization: (parameters: Authorization) => Promise<...>; ... 5 more ...; type: "local"; }>>'. [2353] 808 - Object literal may only specify known properties, and 'apiKey' does not exist in type 'AlchemyLightAccountClientConfig<LocalAccountSigner<{ address: `0x${string}`; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: `0x${string}`; }) => Promise<`0x${string}`>; experimental_signAuthorization: (parameters: Authorization) => Promise<...>; ... 5 more ...; type: "local"; }>>'.

That is all! Now, you can create a smart account client to connect with the upgraded account as a Modular Account.

example.ts
// @filename: lightAccountClient.ts
import { createLightAccountAlchemyClient } from "@account-kit/smart-contracts";
import { sepolia } from "@account-kit/infra";
import { LocalAccountSigner } from "@aa-sdk/core";
import { generatePrivateKey } from "viem/accounts";
 
export const lightAccountClient = await createLightAccountAlchemyClient({
  apiKey: "YOUR_API_KEY",
  chain: sepolia,
  signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
});
// @filename: upgradedAccount.ts
 
// @filename: lightAccountClient.ts
import { createLightAccountAlchemyClient } from "@account-kit/smart-contracts";
import { sepolia } from "@account-kit/infra";
import { LocalAccountSigner } from "@aa-sdk/core";
import { generatePrivateKey } from "viem/accounts";
 
export const lightAccountClient = await createLightAccountAlchemyClient({
  apiKey: "YOUR_API_KEY",
  chain: sepolia,
  signer: LocalAccountSigner.privateKeyToAccountSigner(generatePrivateKey()),
});
// @filename: example.js
 
// ---cut---
import { lightAccountClient } from "./lightAccountClient";
import { getMSCAUpgradeToData } from "@account-kit/smart-contracts";
 
const { createMAAccount, ...upgradeToData } = await getMSCAUpgradeToData(
  lightAccountClient,
  { account: lightAccountClient.account }
);
 
const hash = await lightAccountClient.upgradeAccount({
  upgradeTo: upgradeToData,
  waitForTx: true,
});
 
export const upgradedAccount = await createMAAccount();
// @filename: example.js
 
// ---cut---
import { createAlchemySmartAccountClient } from "@account-kit/infra";
import { multiOwnerPluginActions } from "@account-kit/smart-contracts";
import { upgradedAccount } from "./upgradedAccount";
 
const upgradedAccountClient = await createAlchemySmartAccountClient({
  apiKey: "YOUR_API_KEY",
  chain: lightAccountClient.chain,
  account: upgradedAccount,
}).extend(multiOwnerPluginActions);
 
const owners = await upgradedAccountClient.readOwners();
## Errors were thrown in the sample, but not included in an error tag These errors were not marked as being expected: 2353. Expected: // @errors: 2353 Compiler Errors: lightAccountClient.ts [2353] 342 - Object literal may only specify known properties, and 'apiKey' does not exist in type 'AlchemyLightAccountClientConfig<LocalAccountSigner<{ address: `0x${string}`; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: `0x${string}`; }) => Promise<`0x${string}`>; experimental_signAuthorization: (parameters: Authorization) => Promise<...>; ... 5 more ...; type: "local"; }>>'. [2353] 842 - Object literal may only specify known properties, and 'apiKey' does not exist in type 'AlchemyLightAccountClientConfig<LocalAccountSigner<{ address: `0x${string}`; nonceManager?: NonceManager | undefined; sign: (parameters: { hash: `0x${string}`; }) => Promise<`0x${string}`>; experimental_signAuthorization: (parameters: Authorization) => Promise<...>; ... 5 more ...; type: "local"; }>>'.