> ## Documentation Index
> Fetch the complete documentation index at: https://docs.safefoundation.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Migration

> Safely upgrade an existing Safe Smart Account proxy to a newer Singleton implementation.

## Overview

As the smart account ecosystem evolves, Safe periodically releases new versions of the **Safe Singleton** contract. Each Safe Smart Account is implemented as a **SafeProxy**, which delegates all logic to a Singleton contract whose address is stored in storage slot `0`.

Existing SafeProxy contracts can be upgraded to point to a newer Singleton implementation. This process preserves the Safe’s address, owners, and configuration, but requires an explicit migration transaction approved by the Safe owners. The migration is performed via `delegatecall` and must be executed with care.

This guide walks through a step-by-step example of migrating an existing SafeProxy to a newer Singleton using the **SafeMigration** contract and the Safe Protocol Kit.

<Warning>
  Only migrate to trusted and audited Singleton implementations. A malicious or incompatible implementation can take control of the SafeProxy, resulting in permanent loss of access and funds. Always verify compatibility between the existing SafeProxy and the target Singleton version before migrating.
</Warning>

## Migration process

The first step in any migration is identifying the address of the target Singleton contract. Safe provides the **SafeMigration** contract, which updates the Singleton address stored in a SafeProxy.

* SafeMigration source:\
  [https://github.com/safe-fndn/safe-smart-account/blob/main/contracts/libraries/SafeMigration.sol](https://github.com/safe-fndn/safe-smart-account/blob/main/contracts/libraries/SafeMigration.sol)
* Official SafeMigration deployments are listed in the\
  [Safe Deployments repository](https://github.com/safe-global/safe-deployments)

## SafeMigration contract methods

The currently available SafeMigration contract supports upgrades to **Safe Singleton v1.4.1**.

### `migrateSingleton()`

Updates the SafeProxy to point to the new L1 Singleton implementation.

### `migrateWithFallbackHandler()`

Updates both the Singleton implementation and the fallback handler.

### `migrateL2Singleton()`

Updates the SafeProxy to point to the L2 Singleton implementation.

### `migrateL2WithFallbackHandler()`

Updates the L2 Singleton implementation and the fallback handler.

## Requirements

* An already deployed SafeProxy contract
* Compatibility between the existing SafeProxy and Singleton v1.4.1
* For simplicity, this example assumes a Safe with a **threshold of 1**

## Migration tutorial

<Steps>
  <Step title="Setup a new project">
    ```bash theme={null}
    mkdir safe-migration-tutorial && cd safe-migration-tutorial
    npm init -y
    npm install @safe-global/protocol-kit @safe-global/types-kit viem
    ```

    Add typescript support to the project:

    ```bash theme={null}
    npm install --save-dev typescript ts-node
    npx tsc --init
    ```
  </Step>

  <Step title="Add script commands in package.json">
    The `SafeMigration` contract provides four methods for migration. Update the `package.json` to add the following script commands:
    The migration script will read the argument and choose the appropriate method to execute.

    ```json theme={null}
    ...
      "scripts": {
        ...
        "migrate:L1": "ts-node ./src/migrate.ts migrateSingleton",
        "migrate:L2": "ts-node ./src/migrate.ts migrateL2Singleton",
        "migrate:L1:withFH": "ts-node ./src/migrate.ts migrateWithFallbackHandler",
        "migrate:L2:withFH": "ts-node ./src/migrate.ts migrateL2WithFallbackHandler"
      },
    ...
    ```
  </Step>

  <Step title="Create a migration script">
    Create a new file `src/migrate.ts` and add the following code:

    ```bash theme={null}
    mkdir src
    touch src/migrate.ts
    ```

    ```typescript theme={null}
    import Safe from "@safe-global/protocol-kit";
    import { MetaTransactionData, OperationType } from "@safe-global/types-kit";
    import { parseAbi, encodeFunctionData, http, createPublicClient } from "viem";

    type MigrationMethod =
      | "migrateSingleton"
      | "migrateWithFallbackHandler"
      | "migrateL2Singleton"
      | "migrateL2WithFallbackHandler";

    async function main(migrationMethod: MigrationMethod) {
        // Define constants
        // Build calldata for the migration
        // Initialize the Protocol Kit
        // Create and execute transaction
    }

    const migrationMethod = process.argv.slice(2)[0] as MigrationMethod;
    main(migrationMethod).catch(console.error);
    ```
  </Step>

  <Step title="Define variables">
    Define the constants required for the migration script. Replace the placeholders with the actual values.

    ```typescript theme={null}
      // Define constants
      const SAFE_ADDRESS = // ...
      const OWNER_PRIVATE_KEY = // ...
      const RPC_URL = // ...
      const SAFE_MIGRATION_CONTRACT_ADDRESS = // ...
      const ABI = parseAbi([
        "function migrateSingleton() public",
        "function migrateWithFallbackHandler() external",
        "function migrateL2Singleton() public",
        "function migrateL2WithFallbackHandler() external",
      ]);
    ```
  </Step>

  <Step title="Build `calldata` for the migration">
    ```typescript theme={null}
      // Build calldata for the migration
      const calldata = encodeFunctionData({
        abi: ABI,
        functionName: migrationMethod,
      });

      const safeTransactionData: MetaTransactionData = {
        to: SAFE_MIGRATION_CONTRACT_ADDRESS,
        value: "0",
        data: calldata,
        operation: OperationType.DelegateCall,
      };
    ```
  </Step>

  <Step title="Initialize the Protocol Kit">
    ```typescript theme={null}
      // Initialize the Protocol Kit
      const preExistingSafe = await Safe.init({
        provider: RPC_URL,
        signer: OWNER_PRIVATE_KEY,
        safeAddress: SAFE_ADDRESS,
      });
    ```
  </Step>

  <Step title="Create and execute transaction">
    ```typescript theme={null}
      // Create and execute transaction
      const safeTransaction = await preExistingSafe.createTransaction({
        transactions: [safeTransactionData],
      });

      console.log(
        `Executing migration method [${migrationMethod}] using Safe [${SAFE_ADDRESS}]`
      );

      const result = await preExistingSafe.executeTransaction(safeTransaction);

      const publicClient = createPublicClient({
        transport: http(RPC_URL),
      });

      console.log(`Transaction hash [${result.hash}]`);

      await publicClient.waitForTransactionReceipt({
        hash: result.hash as `0x${string}`,
      });
    ```
  </Step>

  <Step title="Final script">
    ```typescript theme={null}
    import Safe from "@safe-global/protocol-kit";
    import { MetaTransactionData, OperationType } from "@safe-global/types-kit";
    import { parseAbi, encodeFunctionData, http, createPublicClient } from "viem";

    type MigrationMethod =
      | "migrateSingleton"
      | "migrateWithFallbackHandler"
      | "migrateL2Singleton"
      | "migrateL2WithFallbackHandler";

    async function main(migrationMethod: MigrationMethod) {
      const SAFE_ADDRESS = // ...
      const OWNER_PRIVATE_KEY = // ...
      const RPC_URL = // ...
      const SAFE_MIGRATION_CONTRACT_ADDRESS = // ...
      const ABI = parseAbi([
        "function migrateSingleton() public",
        "function migrateWithFallbackHandler() external",
        "function migrateL2Singleton() public",
        "function migrateL2WithFallbackHandler() external",
      ]);

      const calldata = encodeFunctionData({
        abi: ABI,
        functionName: migrationMethod,
      });

      const safeTransactionData: MetaTransactionData = {
        to: SAFE_MIGRATION_CONTRACT_ADDRESS,
        value: "0",
        data: calldata,
        operation: OperationType.DelegateCall,
      };

      const preExistingSafe = await Safe.init({
        provider: RPC_URL,
        signer: OWNER_PRIVATE_KEY,
        safeAddress: SAFE_ADDRESS,
      });

      const safeTransaction = await preExistingSafe.createTransaction({
        transactions: [safeTransactionData],
      });

      console.log(
        `Executing migration method [${migrationMethod}] using Safe [${SAFE_ADDRESS}]`
      );

      const result = await preExistingSafe.executeTransaction(safeTransaction);

      const publicClient = createPublicClient({
        transport: http(RPC_URL),
      });

      console.log(`Transaction hash [${result.hash}]`);
      await publicClient.waitForTransactionReceipt({
        hash: result.hash as `0x${string}`,
      });
    }

    const migrationMethod = process.argv.slice(2)[0] as MigrationMethod;
    main(migrationMethod).catch(console.error);

    ```
  </Step>

  <Step title="Run the migration script">
    Run one of the below commands:

    ```bash theme={null}
    npm run migrate:L1
    ```

    ```bash theme={null}
    npm run migrate:L2
    ```

    ```bash theme={null}
    npm run migrate:L1:withFH
    ```

    ```bash theme={null}
    npm run migrate:L2:withFH
    ```
  </Step>
</Steps>

## Further actions

* The migration script can be extended to support Safe Account migration with a threshold of more than one. Users can use the [Safe API Kit](https://github.com/safe-global/safe-core-sdk/tree/main/packages/api-kit) to propose the transactions, fetch transaction data, and sign them.
* The source code for this script is available in the [Safe Migration Script repository](https://github.com/5afe/safe-migration-script).
