Skip to content

This guide provides a detailed comparison between two approaches for implementing Kernel-based smart wallets with different relay services, focusing on the minimal code changes required to switch between implementations.

Notifications You must be signed in to change notification settings

gelatodigital/ultrarelay-gelatorelay-migration

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 

Repository files navigation

UltraRelay to GelatoRelay Migration Guide for Kernel Smart Wallets

This guide provides a detailed comparison between using UltraRelay vs GelatoRelay with Kernel-based smart wallets, focusing on the minimal code changes required to switch relay services while maintaining the same account types.

Overview

This migration guide covers switching from UltraRelay to GelatoRelay while keeping the same Kernel account implementations:

Migration 1: Kernel Account with EIP-7702 support

  • Account Type: Kernel Account with EIP-7702 support (unchanged)
  • Relay Change: UltraRelay → GelatoRelay
  • SDK Change: ZeroDev SDK → Gelato Smart Wallet SDK

Migration 2: Kernel Account (ERC-4337)

  • Account Type: Kernel Account (ERC-4337) (unchanged)
  • Relay Change: UltraRelay → GelatoRelay
  • SDK Change: ZeroDev SDK → Gelato Smart Wallet SDK

Key Points

  • Account Type: Remains the same (Kernel accounts)
  • Primary Change: Relay service from UltraRelay to GelatoRelay
  • Secondary Change: SDK from ZeroDev to Gelato (required for GelatoRelay integration)
  • Functionality: Same smart wallet capabilities, different relay infrastructure

Migration Overview

This guide focuses on relay service migration - switching from UltraRelay to GelatoRelay while maintaining the same Kernel account types. The SDK change is necessary because GelatoRelay requires the Gelato Smart Wallet SDK for integration.

What stays the same:

  • Kernel account types (EIP-7702 or ERC-4337)
  • Smart wallet functionality and capabilities
  • User experience and transaction flow

What changes:

  • Relay service: UltraRelay → GelatoRelay
  • SDK: ZeroDev SDK → Gelato Smart Wallet SDK
  • API endpoints and configuration

Migration 1: Kernel Account with EIP-7702 support (UltraRelay → GelatoRelay)

Core Differences

1. Account Creation

ZeroDev SDK + UltraRelay

// Step 1: Create entry point and kernel version
const entryPoint = getEntryPoint("0.7");
const kernelVersion = KERNEL_V3_3_BETA;

// Step 2: Create wallet client for authorization
const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(""),
});

// Step 3: Sign authorization for EIP-7702
const authorization = await walletClient.signAuthorization({
  account,
  contractAddress: KernelVersionToAddressesMap[kernelVersion].accountImplementationAddress,
});

// Step 4: Create validator
const validator = await signerToEcdsaValidator(publicClient, {
  entryPoint,
  kernelVersion,
  signer: account,
});

// Step 5: Create kernel account
const kernelAccount = await createKernelAccount(publicClient as any, {
  address: account.address,
  eip7702Auth: authorization,
  entryPoint,
  kernelVersion,
  plugins: { sudo: validator },
});

Gelato Smart Wallet SDK + Kernel Wallet

// Single step: Create kernel account using Gelato's kernel function
const account = await kernel({
  owner: signer,
  client: publicClient,
  eip7702: true,
});

Migration Change: Replace the 5-step ZeroDev account creation with Gelato's single kernel() function call.

2. Client Creation

ZeroDev SDK + Ultra Relay

const kernelClient = createKernelAccountClient({
  account: kernelAccount,
  chain: baseSepolia,
  bundlerTransport: http(process.env.NEXT_PUBLIC_ULTRA_RELAY_URL || ""),
  paymaster: undefined,
  userOperation: {
    estimateFeesPerGas: async ({ bundlerClient }) => {
      return getUserOperationGasPrice(bundlerClient);
    },
  },
});

Gelato Smart Wallet SDK + Kernel Wallet

// Step 1: Create wallet client
const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(""),
});

// Step 2: Create Gelato smart wallet client
const smartWalletClient = await createGelatoSmartWalletClient(
  walletClient,
  {
    apiKey: process.env.NEXT_PUBLIC_SPONSOR_API_KEY || "",
  }
);

Migration Change: Replace ZeroDev's createKernelAccountClient with Gelato's two-step process using createWalletClient and createGelatoSmartWalletClient.

3. Call Data Preparation

ZeroDev SDK + Ultra Relay

const calls = [
    {
      to: "0x0000000000000000000000000000000000000000" as `0x${string}`,
      value: BigInt(0),
      data: "0x",
    },
  ] as const;

const callData = await kernelClient.account.encodeCalls(calls);

Gelato Smart Wallet SDK + Kernel Wallet

const calls = [
    {
      to: "0x0000000000000000000000000000000000000000" as `0x${string}`,
      value: BigInt(0),
      data: "0x",
    },
  ];

const preparedCalls = await smartWalletClient.prepare({
  payment: sponsored(process.env.NEXT_PUBLIC_SPONSOR_API_KEY || ""),
  calls,
});

Migration Change:

  • Replace ZeroDev's encodeCalls() with Gelato's prepare() method

4. Transaction Execution

ZeroDev SDK + Ultra Relay

const userOpHash = await kernelClient.sendUserOperation({
  callData,
  maxFeePerGas: BigInt(0),
  maxPriorityFeePerGas: BigInt(0),
});

const userOpReceipt = await kernelClient.waitForUserOperationReceipt({
  hash: userOpHash,
});
const hash = userOpReceipt.receipt.transactionHash;

Gelato Smart Wallet SDK + Kernel Wallet

const results = await smartWalletClient.send({ preparedCalls });
const hash = await results?.wait();

Migration Change:

  • Replace ZeroDev's sendUserOperation() and waitForUserOperationReceipt() with Gelato's send() and wait()
  • Change from userOpHash to results.id for logging

Complete Migration Example

From ZeroDev to Gelato

// BEFORE: ZeroDev SDK + Ultra Relay
const entryPoint = getEntryPoint("0.7");
const kernelVersion = KERNEL_V3_3_BETA;
const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(""),
});
const authorization = await walletClient.signAuthorization({
  account,
  contractAddress: KernelVersionToAddressesMap[kernelVersion].accountImplementationAddress,
});
const validator = await signerToEcdsaValidator(publicClient, {
  entryPoint,
  kernelVersion,
  signer: account,
});
const kernelAccount = await createKernelAccount(publicClient as any, {
  address: account.address,
  eip7702Auth: authorization,
  entryPoint,
  kernelVersion,
  plugins: { sudo: validator },
});

const kernelClient = createKernelAccountClient({
  account: kernelAccount,
  chain: baseSepolia,
  bundlerTransport: http(process.env.NEXT_PUBLIC_ULTRA_RELAY_URL || ""),
  paymaster: undefined,
  userOperation: {
    estimateFeesPerGas: async ({ bundlerClient }) => {
      return getUserOperationGasPrice(bundlerClient);
    },
  },
});


const calls = [
    {
      to: "0x0000000000000000000000000000000000000000" as `0x${string}`,
      value: BigInt(0),
      data: "0x",
    },
  ] as const;

const callData = await kernelClient.account.encodeCalls(calls);

const userOpHash = await kernelClient.sendUserOperation({
  callData,
  maxFeePerGas: BigInt(0),
  maxPriorityFeePerGas: BigInt(0),
});
const userOpReceipt = await kernelClient.waitForUserOperationReceipt({
  hash: userOpHash,
});
const hash = userOpReceipt.receipt.transactionHash;
// AFTER: Gelato Smart Wallet SDK + Kernel Wallet
const account = await kernel({
  owner: signer,
  client: publicClient,
  eip7702: true,
});

const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(""),
});

const smartWalletClient = await createGelatoSmartWalletClient(
  walletClient,
  {
    apiKey: process.env.SPONSOR_API_KEY || "",
  }
);

const calls = [
    {
      to: "0x0000000000000000000000000000000000000000" as `0x${string}`,
      value: BigInt(0),
      data: "0x",
    },
  ];

const preparedCalls = await smartWalletClient.prepare({
  payment: sponsored(process.env.SPONSOR_API_KEY || ""),
  calls,
});

const results = await smartWalletClient.send({ preparedCalls });
const hash = await results?.wait();

Migration Checklist

1. Dependencies

  • Remove @zerodev/sdk imports
  • Add @gelatonetwork/smartwallet imports

2. Account Creation

  • Replace ZeroDev account creation with Gelato's kernel() function
  • Remove entry point and kernel version configuration
  • Remove authorization signing
  • Remove validator creation

3. Client Setup

  • Replace createKernelAccountClient with createWalletClient + createGelatoSmartWalletClient
  • Update bundler transport configuration
  • Remove paymaster configuration

4. Transaction Flow

  • Replace encodeCalls() with prepare()
  • Replace sendUserOperation() with send()
  • Replace waitForUserOperationReceipt() with wait()
  • Update transaction hash extraction

5. Error Handling

  • Update error messages and logging
  • Ensure retry logic remains compatible

Summary

The main differences when migrating from UltraRelay to GelatoRelay are:

  1. Account Creation: ZeroDev requires 5 steps vs Gelato's single function call
  2. Client Setup: ZeroDev uses one client vs Gelato's two-client approach
  3. Transaction Flow: ZeroDev uses encodeCalls() + sendUserOperation() vs Gelato's prepare() + send()
  4. Relay Service: UltraRelay → GelatoRelay (primary change)
  5. SDK: ZeroDev SDK → Gelato Smart Wallet SDK (required for GelatoRelay integration)

Migration 2: Kernel Account (ERC-4337) (UltraRelay → GelatoRelay)

Core Differences

1. Account Creation

ZeroDev SDK + Ultra Relay

// Step 1: Get entry point and kernel version
const entryPoint = getEntryPoint("0.7");
const kernelVersion = KERNEL_V3_1;

// Step 2: Create wallet client for authorization
const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(""),
});

// Step 3: Create validator
const validator = await signerToEcdsaValidator(publicClient, {
  entryPoint,
  kernelVersion,
  signer: account,
});

// Step 4: Create kernel account
const kernelAccount = await createKernelAccount(publicClient as any, {
  entryPoint,
  kernelVersion,
  plugins: { sudo: validator },
});

Gelato Smart Wallet SDK + Kernel Wallet

// Single step: Create kernel account using Gelato's kernel function
const account = await kernel({
  owner: signer,
  client: publicClient,
  eip7702: false,
});

Migration Change: Replace the 4-step ZeroDev account creation with Gelato's single kernel() function call.

2. Client Creation

ZeroDev SDK + Ultra Relay

const kernelClient = createKernelAccountClient({
  account: kernelAccount,
  chain: baseSepolia,
  bundlerTransport: http(process.env.NEXT_PUBLIC_ULTRA_RELAY_URL || ""),
  paymaster: undefined,
  userOperation: {
    estimateFeesPerGas: async ({ bundlerClient }) => {
      return getUserOperationGasPrice(bundlerClient);
    },
  },
});

Gelato Smart Wallet SDK + Kernel Wallet

// Step 1: Create wallet client
const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(""),
});

// Step 2: Create Gelato smart wallet client
const smartWalletClient = await createGelatoSmartWalletClient(
  walletClient,
  {
    apiKey: process.env.NEXT_PUBLIC_SPONSOR_API_KEY || "",
  }
);

Migration Change: Replace ZeroDev's createKernelAccountClient with Gelato's two-step process using createWalletClient and createGelatoSmartWalletClient.

3. Call Data Preparation

ZeroDev SDK + Ultra Relay

const calls = [
    {
      to: "0x0000000000000000000000000000000000000000" as `0x${string}`,
      value: BigInt(0),
      data: "0x",
    },
  ] as const;

const callData = await kernelClient.account.encodeCalls(calls);

Gelato Smart Wallet SDK + Kernel Wallet

const calls = [
    {
      to: "0x0000000000000000000000000000000000000000" as `0x${string}`,
      value: BigInt(0),
      data: "0x",
    },
  ];

const preparedCalls = await smartWalletClient.prepare({
  payment: sponsored(process.env.NEXT_PUBLIC_SPONSOR_API_KEY || ""),
  calls,
});

Migration Change:

  • Replace ZeroDev's encodeCalls() with Gelato's prepare() method

4. Transaction Execution

ZeroDev SDK + Ultra Relay

const userOpHash = await kernelClient.sendUserOperation({
  callData,
  maxFeePerGas: BigInt(0),
  maxPriorityFeePerGas: BigInt(0),
});

const userOpReceipt = await kernelClient.waitForUserOperationReceipt({
  hash: userOpHash,
});
const hash = userOpReceipt.receipt.transactionHash;

Gelato Smart Wallet SDK + Kernel Wallet

const results = await smartWalletClient.send({ preparedCalls });
const hash = await results?.wait();

Migration Change:

  • Replace ZeroDev's sendUserOperation() and waitForUserOperationReceipt() with Gelato's send() and wait()
  • Change from userOpHash to results.id for logging

Complete Migration Example

From ZeroDev to Gelato

// BEFORE: ZeroDev SDK + Ultra Relay
const entryPoint = getEntryPoint("0.7");
const kernelVersion = KERNEL_V3_3_BETA;
const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(""),
});

const validator = await signerToEcdsaValidator(publicClient, {
  entryPoint,
  kernelVersion,
  signer: account,
});
const kernelAccount = await createKernelAccount(publicClient as any, {
  entryPoint,
  kernelVersion,
  plugins: { sudo: validator },
});

const kernelClient = createKernelAccountClient({
  account: kernelAccount,
  chain: baseSepolia,
  bundlerTransport: http(process.env.NEXT_PUBLIC_ULTRA_RELAY_URL || ""),
  paymaster: undefined,
  userOperation: {
    estimateFeesPerGas: async ({ bundlerClient }) => {
      return getUserOperationGasPrice(bundlerClient);
    },
  },
});


const calls = [
    {
      to: "0x0000000000000000000000000000000000000000" as `0x${string}`,
      value: BigInt(0),
      data: "0x",
    },
  ] as const;

const callData = await kernelClient.account.encodeCalls(calls);

const userOpHash = await kernelClient.sendUserOperation({
  callData,
  maxFeePerGas: BigInt(0),
  maxPriorityFeePerGas: BigInt(0),
});
const userOpReceipt = await kernelClient.waitForUserOperationReceipt({
  hash: userOpHash,
});
const hash = userOpReceipt.receipt.transactionHash;
// AFTER: Gelato Smart Wallet SDK + Kernel Wallet
const account = await kernel({
  owner: signer,
  client: publicClient,
  eip7702: false,
});

const walletClient = createWalletClient({
  account,
  chain: baseSepolia,
  transport: http(""),
});

const smartWalletClient = await createGelatoSmartWalletClient(
  walletClient,
  {
    apiKey: process.env.SPONSOR_API_KEY || "",
  }
);

const calls = [
    {
      to: "0x0000000000000000000000000000000000000000" as `0x${string}`,
      value: BigInt(0),
      data: "0x",
    },
  ];

const preparedCalls = await smartWalletClient.prepare({
  payment: sponsored(process.env.SPONSOR_API_KEY || ""),
  calls,
});

const results = await smartWalletClient.send({ preparedCalls });
const hash = await results?.wait();

Migration Checklist

1. Dependencies

  • Remove @zerodev/sdk imports
  • Add @gelatonetwork/smartwallet imports

2. Account Creation

  • Replace ZeroDev account creation with Gelato's kernel() function
  • Remove entry point and kernel version configuration
  • Remove validator creation

3. Client Setup

  • Replace createKernelAccountClient with createWalletClient + createGelatoSmartWalletClient
  • Update bundler transport configuration
  • Remove paymaster configuration

4. Transaction Flow

  • Replace encodeCalls() with prepare()
  • Replace sendUserOperation() with send()
  • Replace waitForUserOperationReceipt() with wait()
  • Update transaction hash extraction

5. Error Handling

  • Update error messages and logging
  • Ensure retry logic remains compatible

Summary

The main differences when migrating from UltraRelay to GelatoRelay are:

  1. Account Creation: ZeroDev requires 4 steps vs Gelato's single function call
  2. Client Setup: ZeroDev uses one client vs Gelato's two-client approach
  3. Transaction Flow: ZeroDev uses encodeCalls() + sendUserOperation() vs Gelato's prepare() + send()
  4. Relay Service: UltraRelay → GelatoRelay (primary change)
  5. SDK: ZeroDev SDK → Gelato Smart Wallet SDK (required for GelatoRelay integration)

About

This guide provides a detailed comparison between two approaches for implementing Kernel-based smart wallets with different relay services, focusing on the minimal code changes required to switch between implementations.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published