Skip to main content
This guide explains how to upgrade to the latest MEE contracts suite. This involves upgrading your Nexus smart accounts from older versions to the latest implementation. The migration process preserves your account’s address, balance, and history while providing access to new features, security fixes, and performance improvements.

Why Upgrade?

Benefits of Upgrading

Upgrading your Nexus smart account is recommended for several important reasons:
1

Security Improvements

The latest implementation includes critical security enhancements.
2

New Features

Access to the newest MEE capabilities.
3

Performance Improvements

Enhanced gas efficiency and transaction processing.
4

Compatibility

Ensure you can leverage all the latest features of AbstractJS SDK and MEE infra.

Migration Process Overview

Migration Steps

The migration consists of these key steps:
1

Update AbstractJS Package

Install the latest SDK version
2

Connect to Existing Account

Use your existing account address
3

Upgrade Implementation

Call upgradeToAndCall() via Supertransaction
4

Verify Upgrade

Test the account by executing a simple Supertransaction
5

Update Application

Adjust your application to use the latest features

Important Notes for Migration

CRITICAL: After migration, you must store your account address and use it with the accountAddress parameter in toNexusAccount() or toMultichainNexusAccount() for all future interactions. This is how you’ll maintain access to your upgraded account and its balances.

Step 1: Update AbstractJS Package

SDK Update

First, update to the latest version of the AbstractJS SDK:
# npm
npm update @biconomy/abstractjs

# yarn
yarn upgrade @biconomy/abstractjs

# pnpm
pnpm update @biconomy/abstractjs

# bun
bun update @biconomy/abstractjs

Step 2: Connect to Your Existing Account

Account Connection

When connecting to an account created with an older SDK version, you’ll need your existing account address:
  import {
    createMeeClient,
    getMEEVersion,
    MEEVersion,
    toMultichainNexusAccount,
  } from "@biconomy/abstractjs";
  import { Hex, http } from "viem";
  import { privateKeyToAccount } from "viem/accounts";
  import { base } from "viem/chains";

  const privateKey: Hex = "0xyour_private_key";
  const eoaAccount = privateKeyToAccount(privateKey);

  // Initialize mcNexus with current version config
  const mcNexus = await toMultichainNexusAccount({
    signer: eoaAccount,
    chainConfigurations: [
      {
        // Your preferred chains
        chain: base,
        // Use a reliable RPC here to avoid RPC issues
        transport: http(),
        // Use the current MEE version here
        version: getMEEVersion(MEEVersion.V2_0_0),
        // This is completely optional if this is the first time your upgrading your Smart account.
        // If you have already upgraded from some X MEE version to Y MEE version ? This field is mandatory
        accountAddress: '0xyour_sca_address',
      },
    ],
  });

  // Create MEE client
  const meeClient = await createMeeClient({
    account: mcNexus,
  });
Note: After upgrading, use your existing accountAddress for all SDK operations.
  • accountAddress is optional on your first upgrade.
  • After any previous upgrade, always provide accountAddress to avoid errors or wrong account access.

Step 3: Upgrade the Smart Account

Account Upgrade

Call the upgradeToAndCall() method to upgrade your account to the latest implementation.
In most cases, your account is going to use newer MEE K1 Validator, so you’ll have to re-initialize it while upgrading the account.
    /**
    * Generate upgrade init data for Nexus account upgrade with new validator initialization
    */
    export function getUpgradeInitData(
        ownerAddress: Address,
        bootstrapAddress: Address,
      ): `0x${string}` {
        // Step 1: Encode initNexusWithDefaultValidator call with owner address
        const bootstrapCall = encodeFunctionData({
          abi: NexusBootstrapAbi,
          functionName: 'initNexusWithDefaultValidator',
          args: [ownerAddress],
        });
      
        // Step 2: Package bootstrap address + call as tuple
        const bootstrapData = encodeAbiParameters(
          [
            { name: 'bootstrap', type: 'address' },
            { name: 'initData', type: 'bytes' },
          ],
          [bootstrapAddress, bootstrapCall]
        );
      
        // Step 3: Wrap in initializeAccount call
        const upgradeInitData = encodeFunctionData({
          abi: parseAbi(['function initializeAccount(bytes initData)']),
          functionName: 'initializeAccount',
          args: [bootstrapData],
        });
      
        return upgradeInitData;
      }

    // New MEE version config for account upgrades
    const newMEEVersionConfig = getMEEVersion(MEEVersion.V2_1_0);
    const newImplementationAddress = newMEEVersionConfig.implementationAddress;
    const newBootstrapAddress = newMEEVersionConfig.bootStrapAddress;

    const upgradeInitData = getUpgradeInitData(
      eoaAccount.address,
      newBootstrapAddress
    );

    const upgradeAbi = parseAbi([
      "function upgradeToAndCall(address newImplementation, bytes data)",
    ]);

    // Build composable instruction to upgrade your Nexus smart account
    const instructions = await mcNexus.buildComposable({
      type: "default",
      data: {
        // Smart account address
        to: mcNexus.addressOn(base.id, true),
        functionName: "upgradeToAndCall",
        args: [newImplementationAddress, upgradeInitData],
        abi: upgradeAbi,
        chainId: base.id,
      },
    });

    const USDC_BASE: Address = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // fee token

    const triggerAmount = 1n;

    // This example follows a fusion mode for upgrading the Nexus smart account via Supertransaction
    // If you wanted to upgrade your account via smart account mode ? Please use getQuote and executeQuote flows
    const quote = await meeClient.getFusionQuote({
      instructions,
      trigger: {
        tokenAddress: USDC_BASE,
        chainId: base.id,
        amount: triggerAmount,
      },
      feeToken: {
        address: USDC_BASE,
        chainId: base.id,
      },
      simulation: {
        simulate: true,
      },
    });

    // Executes the supertransaction
    const { hash: supertxHash } = await meeClient.executeFusionQuote({
      fusionQuote: quote,
    });

    // Wait for the supertransaction
    const receipt = await meeClient.waitForSupertransactionReceipt({
      hash: supertxHash,
    });

Step 4: Verify the Upgrade

Upgrade Verification

After upgrading, verify that your account works correctly by performing a test supertransaction:
    import {
      createMeeClient,
      getMEEVersion,
      MEEVersion,
      toMultichainNexusAccount,
    } from "@biconomy/abstractjs";
    import { Address, Hex, http } from "viem";
    import { privateKeyToAccount } from "viem/accounts";
    import { base } from "viem/chains";

    const privateKey: Hex = "0xyour_private_key";
    const storedSmartAccountAddress: Address = "0xyour_sca_address";
    const eoaAccount = privateKeyToAccount(privateKey);

    // Initialize mcNexus with new version config
    const mcNexus = await toMultichainNexusAccount({
      signer: eoaAccount,
      chainConfigurations: [
        {
          chain: base,
          transport: http(),
          version: getMEEVersion(MEEVersion.V2_1_0),
          // This is essential
          accountAddress: storedSmartAccountAddress,
        },
      ],
    });

    // Create MEE client
    const meeClient = await createMeeClient({
      account: mcNexus,
    });

    const USDC_BASE: Address = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"; // fee token

    const amount = 1n;
    const instructions = await mcNexus.buildComposable({
      type: "transfer",
      data: {
        tokenAddress: USDC_BASE,
        chainId: base.id,
        amount,
        recipient: eoaAccount.address,
      },
    });

    // Get quotes
    const quote = await meeClient.getFusionQuote({
      instructions,
      trigger: {
        tokenAddress: USDC_BASE,
        chainId: base.id,
        amount,
      },
      feeToken: {
        address: USDC_BASE,
        chainId: base.id,
      },
    });

    // Executes the supertransaction
    const { hash: supertxHash } = await meeClient.executeFusionQuote({
      fusionQuote: quote,
    });

    // Wait for the supertransaction
    const receipt = await meeClient.waitForSupertransactionReceipt({
      hash: supertxHash,
    });

Step 5: Store Your Account Address

Address Storage

After successful migration, you MUST store your account address for future use:
// Get and store your account address
const accountAddress = mcNexus.addressOn(base.id, true);
console.log("IMPORTANT - Store this account address:", accountAddress);

// In your application, store this address
// (database, local storage, user profile, etc.)
storeAccountAddress(userIdentifier, accountAddress);

Post-Migration: Future Account Access

Account Access After Migration

After migration, ALL future interactions with your account must use the accountAddress parameter:

CORRECT WAY

to access your account after migration
  // CORRECT WAY to access your account after migration

  const mcNexus = await toMultichainNexusAccount({
    signer: eoaAccount,
    chainConfigurations: [
      {
        chain: base,
        transport: http(),
        version: getMEEVersion(MEEVersion.V2_1_0),
        accountAddress: "YOUR_STORED_ACCOUNT_ADDRESS" // This is essential
      },
    ],
  });

INCORRECT WAY

this will create a new account, not access your existing one
 // INCORRECT WAY - this will create a new account, not access your existing one

   const mcNexus = await toMultichainNexusAccount({
     signer: eoaAccount,
     chainConfigurations: [
       {
         chain: base,
         transport: http(),
         version: getMEEVersion(MEEVersion.V2_1_0),
         // Missing account address field
       },
     ],
   });

Example Code

A minimal working migration script is available on GitHub: Click here

Troubleshooting

Common Issues and Solutions

If you encounter issues during the migration:
1

Check Funds

Make sure you’re re-initializing or not re-initializing the validator module depending on the version you are migrating from and to. See two tabs on the Step 3.
2

Check Funds

Ensure the account has sufficient funds for gas or use sponsorship mode for gasless upgrades
3

Verify Deployment

Verify the account is properly deployed on-chain
4

Check SDK Version

Check that your application is using the latest SDK version

Next Steps

After successfully migrating your Nexus account:

Post-Migration Actions

1

Store Account Address

STORE YOUR ACCOUNT ADDRESS in your application’s persistent storage
2

Update Application Code

Update your application code to use the accountAddress parameter in all future interactions
3

Test Thoroughly

Test thoroughly with real transactions to ensure everything works as expected
By following this migration guide and properly storing your account address, you’ve successfully upgraded your Nexus account to the latest implementation while preserving its address, balance, and history.