Skip to main content

Overview

Modern blockchain applications need to support multiple networks and blockchain ecosystems. Magic’s SDK provides a unified approach for multichain support by configuring a single Magic instance with multiple extensions:
  • EVM Extensions: Support multiple EVM-compatible chains with seamless switching
  • Blockchain Extensions: Support non-EVM chains like Solana, Bitcoin, Hedera, and more
This allows you to interact with Polygon, Optimism, Solana, Hedera, and other networks all from one Magic SDK instance.

Complete Multichain Setup

Configure a single Magic instance to support multiple blockchain networks:
JavaScript
import { Magic } from 'magic-sdk';
import { EVMExtension } from '@magic-ext/evm';
import { SolanaExtension } from '@magic-ext/solana';
import { HederaExtension } from '@magic-ext/hedera';
import { ethers } from 'ethers';

const magic = new Magic('YOUR_API_KEY', {
  extensions: [
    // EVM chain switching for Polygon and Optimism
    new EVMExtension([
      {
        rpcUrl: 'https://polygon-rpc.com/',
        chainId: 137,
        default: true,
      },
      {
        rpcUrl: 'https://mainnet.optimism.io',
        chainId: 10,
      },
    ]),
    // Blockchain extensions for Solana and Hedera
    new SolanaExtension({
      rpcUrl: 'https://api.mainnet-beta.solana.com',
    }),
    new HederaExtension({
      network: 'mainnet',
    }),
  ],
});

// Switch to Optimism network
await magic.evm.switchChain(10);

// Get current EVM address (works for any EVM chain)
const provider = new ethers.BrowserProvider(magic.rpcProvider);
const accounts = await provider.listAccounts();
const evmAddress = accounts[0].address;

// Get Solana address
const solanaAddress = await magic.solana.getPublicAddress();

// Get Hedera address
const hederaAddress = await magic.hedera.getPublicAddress();

Network Management in Your Application

Implement a comprehensive network management system for your unified multichain Magic instance:
TypeScript
import React, { createContext, useState, useContext, useEffect } from 'react';
import { Magic } from 'magic-sdk';
import { EVMExtension } from '@magic-ext/evm';
import { SolanaExtension } from '@magic-ext/solana';
import { HederaExtension } from '@magic-ext/hedera';
import { ethers } from 'ethers';

// Define supported networks
export enum NetworkType {
  EVM = 'evm',
  SOLANA = 'solana',
  HEDERA = 'hedera',
}

export interface Network {
  type: NetworkType;
  chainId?: number; // For EVM chains
  name: string;
  rpcUrl?: string;
}

const SUPPORTED_NETWORKS: Network[] = [
  { type: NetworkType.EVM, chainId: 137, name: 'Polygon' },
  { type: NetworkType.EVM, chainId: 10, name: 'Optimism' },
  { type: NetworkType.SOLANA, name: 'Solana' },
  { type: NetworkType.HEDERA, name: 'Hedera' },
];

interface NetworkContextType {
  currentNetwork: Network;
  magicInstance: Magic | null;
  switchNetwork: (network: Network) => Promise<void>;
  getPublicAddress: () => Promise<string>;
}

const NetworkContext = createContext<NetworkContextType | undefined>(undefined);

export const NetworkProvider = ({ children }: {children: React.ReactNode}) => {
  const [currentNetwork, setCurrentNetwork] = useState<Network>(SUPPORTED_NETWORKS[0]);
  const [magicInstance, setMagicInstance] = useState<Magic | null>(null);
  
  // Initialize Magic instance
  useEffect(() => {
    const magic = new Magic('YOUR_API_KEY', {
      extensions: [
        new EVMExtension([
          { rpcUrl: 'https://polygon-rpc.com/', chainId: 137, default: true },
          { rpcUrl: 'https://mainnet.optimism.io', chainId: 10 },
        ]),
        new SolanaExtension({
          rpcUrl: 'https://api.mainnet-beta.solana.com',
        }),
        new HederaExtension({
          rpcUrl: 'https://mainnet-public.mirrornode.hedera.com',
        }),
      ],
    });
    setMagicInstance(magic);
  }, []);
  
  const switchNetwork = async (network: Network) => {
    if (!magicInstance) return;
    
    if (network.type === NetworkType.EVM && network.chainId) {
      // Switch EVM chain
      await magicInstance.evm.switchChain(network.chainId);
    }
    
    setCurrentNetwork(network);
  };
  
  const getPublicAddress = async (): Promise<string> => {
    if (!magicInstance) throw new Error('Magic instance not initialized');
    
    if (currentNetwork.type === NetworkType.EVM) {
      // Use ethers for EVM chains
      const provider = new ethers.BrowserProvider(magicInstance.rpcProvider);
      const accounts = await provider.listAccounts();
      return accounts[0].address;
    } else if (currentNetwork.type === NetworkType.SOLANA) {
      // Use Solana extension
      return await magicInstance.solana.getPublicAddress();
    } else if (currentNetwork.type === NetworkType.HEDERA) {
      // Use Hedera extension
      return await magicInstance.hedera.getPublicAddress();
    }
    
    throw new Error('Unsupported network type');
  };
  
  return (
    <NetworkContext.Provider value={{ 
      currentNetwork, 
      magicInstance, 
      switchNetwork,
      getPublicAddress
    }}>
      {children}
    </NetworkContext.Provider>
  );
};

export const useNetwork = () => {
  const context = useContext(NetworkContext);
  if (context === undefined) {
    throw new Error('useNetwork must be used within a NetworkProvider');
  }
  return context;
};

Network Selector Component

Create a comprehensive network selector that handles both EVM chains and blockchain extensions:
TypeScript
import React from 'react';
import { useNetwork } from './NetworkContext';
import { SUPPORTED_NETWORKS } from './NetworkContext';

export const NetworkSelector = () => {
  const { currentNetwork, switchNetwork } = useNetwork();
  
  return (
    <div className="network-selector">
      <label htmlFor="network-select">Current Network:</label>
      <select 
        id="network-select"
        value={`${currentNetwork.type}-${currentNetwork.chainId || 'default'}`}
        onChange={(e) => {
          const [type, chainId] = e.target.value.split('-');
          const network = SUPPORTED_NETWORKS.find(n => 
            n.type === type && 
            (chainId === 'default' ? !n.chainId : n.chainId === parseInt(chainId))
          );
          if (network) switchNetwork(network);
        }}
      >
        {SUPPORTED_NETWORKS.map((network) => (
          <option 
            key={`${network.type}-${network.chainId || 'default'}`} 
            value={`${network.type}-${network.chainId || 'default'}`}
          >
            {network.name}
          </option>
        ))}
      </select>
    </div>
  );
};

Performing Network-Specific Operations

Handle operations across different network types:
TypeScript
import { useNetwork } from './NetworkContext';
import { ethers } from 'ethers';

export const WalletOperations = () => {
  const { currentNetwork, magicInstance, getPublicAddress } = useNetwork();
  
  const handleGetAddress = async () => {
    try {
      const address = await getPublicAddress();
      console.log(`${currentNetwork.name} Address:`, address);
    } catch (error) {
      console.error('Failed to get address:', error);
    }
  };
  
  const handleRevealPrivateKey = async () => {
    if (!magicInstance) return;
    
    try {
      if (currentNetwork.type === NetworkType.EVM) {
        // For EVM chains, use the user module
        await magicInstance.user.revealEVMPrivateKey();
      } else if (currentNetwork.type === NetworkType.SOLANA) {
        // For Solana, use the extension
        await magicInstance.solana.revealPrivateKey();
      } else if (currentNetwork.type === NetworkType.HEDERA) {
        // For Hedera, use the extension
        await magicInstance.hedera.revealPrivateKey();
      }
    } catch (error) {
      console.error('Failed to reveal private key:', error);
    }
  };
  
  const handleSendTransaction = async (recipient: string, amount: string) => {
    if (!magicInstance) return;
    
    try {
      if (currentNetwork.type === NetworkType.EVM) {
        // EVM transaction using ethers
        const provider = new ethers.BrowserProvider(magicInstance.rpcProvider);
        const signer = provider.getSigner();
        
        const tx = await signer.sendTransaction({
          to: recipient,
          value: ethers.parseEther(amount)
        });
        
        console.log(`EVM Transaction sent:`, tx.hash);
        return tx;
      } else {
        // Non-EVM chains would require chain-specific transaction logic
        console.log('Non-EVM transaction logic would go here');
      }
    } catch (error) {
      console.error('Transaction failed:', error);
      throw error;
    }
  };
  
  return (
    <div className="wallet-operations">
      <button onClick={handleGetAddress}>
        Get {currentNetwork.name} Address
      </button>
      <button onClick={handleRevealPrivateKey}>
        Reveal Private Key
      </button>
      {/* Additional UI components */}
    </div>
  );
};

Resources

I