Skip to main content

Live Demo

Explore a fully working implementation in this live demo:
This demo includes a complete working implementation with Starknet wallets integration using Dynamic Labs SDK. You can explore code at this repository.

Quick Start

Installation

Install the Layerswap Widget along with the necessary wallet providers. This installation includes support for Starknet, Ethereum, Solana, and Bitcoin wallets:
npm install @layerswap/widget zustand @layerswap/wallet-starknet @layerswap/wallet-evm wagmi viem @tanstack/react-query @layerswap/wallet-svm @layerswap/wallet-bitcoin @bigmi/client @bigmi/core @bigmi/react
If you want to customize the wallet provider selection, please refer to the Widget Quickstart.

Initial Configuration

Create a custom Starknet wallet connection hook that bridges Dynamic Labs SDK with Layerswap Widget.
import { useCallback, useEffect, useMemo } from "react";
import {
  useUserWallets,
  useDynamicContext,
  dynamicEvents,
  Wallet as DynamicWallet,
} from "@dynamic-labs/sdk-react-core";
import {
  resolveWalletConnectorIcon,
  NetworkWithTokens,
} from "@layerswap/widget";
import { WalletConnectionProvider, Wallet, WalletConnectionProviderProps } from "@layerswap/widget/types"

export default function useStarknet({ networks }: WalletConnectionProviderProps): WalletConnectionProvider {
  const name = "Starknet";
  const id = "starknet";

  // Dynamic SDK
  const { setShowAuthFlow, handleLogOut } = useDynamicContext();
  const userWallets = useUserWallets();

  // Starknet network names
  const starknetNetworkNames = [
    "STARKNET_MAINNET",
    "STARKNET_SEPOLIA",
  ]

  // Supported-networks
  const supportedNetworks = useMemo(
    () => ({
      asSource: starknetNetworkNames,
      autofill: starknetNetworkNames,
      withdrawal: starknetNetworkNames,
    }),
    [starknetNetworkNames],
  );

  // Clean up dynamicEvents listeners on unmount
  useEffect(() => {
    return () => {
      dynamicEvents.removeAllListeners("walletAdded");
      dynamicEvents.removeAllListeners("authFlowCancelled");
    };
  }, []);
  // connectWallet: log out existing, show authFlow, wait for event, then resolve
  const connectWallet = useCallback(async (): Promise<Wallet | undefined> => {
    if (userWallets.length) {
      await handleLogOut();
    }

    const newDynWallet = await new Promise<DynamicWallet>((resolve, reject) => {
      setShowAuthFlow(true);

      const onAdded = (w: DynamicWallet) => {
        cleanup();
        resolve(w);
      };
      const onCancelled = () => {
        cleanup();
        reject(new Error("User cancelled the connection"));
      };
      const cleanup = () => {
        dynamicEvents.off("walletAdded", onAdded);
        dynamicEvents.off("authFlowCancelled", onCancelled);
      };

      dynamicEvents.on("walletAdded", onAdded);
      dynamicEvents.on("authFlowCancelled", onCancelled);
    });

    return resolveWallet({
      connection: newDynWallet,
      networks,
      supportedNetworks,
      disconnect: handleLogOut,
      providerName: name,
    });
  }, [userWallets, handleLogOut, setShowAuthFlow, networks, supportedNetworks]);

  // Logout
  const disconnectWallets = useCallback(async () => {
    await handleLogOut();
  }, [handleLogOut]);

  // Map wagmi connectors → Dynamic SDK wallets → our Wallet shape
  const connectedWallets: Wallet[] = useMemo(
    () =>
      userWallets
        .map((dyn) => {
          if (!dyn) return;
          return resolveWallet({
            connection: dyn,
            networks,
            supportedNetworks,
            disconnect: disconnectWallets,
            providerName: name,
          });
        })
        .filter(Boolean) as Wallet[],
    [userWallets, networks, supportedNetworks, disconnectWallets],
  );

  const logo = networks.find((n) => n.name.toLowerCase().includes("starknet"))?.logo;

  return {
    connectWallet,
    activeWallet: connectedWallets.find((w) => w.isActive),
    connectedWallets,
    asSourceSupportedNetworks: supportedNetworks.asSource,
    autofillSupportedNetworks: supportedNetworks.autofill,
    withdrawalSupportedNetworks: supportedNetworks.withdrawal,
    name,
    id,
    providerIcon: logo,
  };
}

/** Reusable helper to turn a DynamicWallet + context into our `Wallet` shape */
function resolveWallet(props: {
  connection: DynamicWallet;
  networks: NetworkWithTokens[];
  supportedNetworks: {
    asSource: string[];
    autofill: string[];
    withdrawal: string[];
  };
  disconnect: () => Promise<void>;
  providerName: string;
}): Wallet | undefined {
  const { connection, networks, supportedNetworks, disconnect, providerName } = props;

  const connectorName = connection.connector.name;
  const address = connection.address;
  if (!connectorName || !address) return;

  const displayName = `${connectorName}${providerName}`;
  const networkIcon = networks.find((n) => n.name.toLowerCase().includes("starknet"))?.logo;

  return {
    id: connectorName,
    isActive: true,
    address,
    addresses: [address],
    displayName,
    providerName,
    icon: resolveWalletConnectorIcon({ iconUrl: connection.connector.metadata.icon }),
    disconnect: () => disconnect(),
    asSourceSupportedNetworks: supportedNetworks.asSource,
    autofillSupportedNetworks: supportedNetworks.autofill,
    withdrawalSupportedNetworks: supportedNetworks.withdrawal,
    networkIcon,
  };
}

Set up the providers and widget in your main component:
import { Swap, LayerswapProvider } from '@layerswap/widget'
import { StarknetWalletConnectors } from "@dynamic-labs/starknet";
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { EVMProvider } from "@layerswap/wallet-evm"
import { StarknetProvider } from "@layerswap/wallet-starknet"
import { SVMProvider } from "@layerswap/wallet-svm"
import { BitcoinProvider } from "@layerswap/wallet-bitcoin"
import useCustomStarknet from "../hooks/useCustomStarknet";
import { WalletProvider } from "@layerswap/widget/types";
import "@layerswap/widget/index.css"

const PageComponent = () => {

    const starknetProvider: WalletProvider = {
        ...StarknetProvider,
        walletConnectionProvider: useCustomStarknet
    }

    return (
        <DynamicContextProvider
            settings={{
                environmentId: {DYNAMIC_ENVIRONMENT_ID},
                walletConnectors: [StarknetWalletConnectors],
            }}
        >
            <LayerswapProvider
                config={{
                    apiKey: {LAYERSWAP_API_KEY}, //optional
                    version: 'mainnet',
                    initialValues: {
                        to: 'STARKNET_MAINNET',
                        toAsset: 'USDC',
                        lockTo: true
                    },
                }}
                walletProviders={[EVMProvider, starknetProvider, SVMProvider, BitcoinProvider]}
            >
                <Swap />
            </LayerswapProvider>
        </DynamicContextProvider>
    )
}
You can generate an API key from the Partner Dashboard to track swaps done through the widget.

Environment Variables

Create a .env.local file with your API credentials:
.env.local
DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id
LAYERSWAP_API_KEY=your_layerswap_api_key
Get Your API Keys:

Complete Example

Here’s a full implementation example combining all concepts:
import { Swap, LayerswapProvider } from '@layerswap/widget'
import { StarknetWalletConnectors } from "@dynamic-labs/starknet";
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { EVMProvider } from "@layerswap/wallet-evm"
import { StarknetProvider } from "@layerswap/wallet-starknet"
import { SVMProvider } from "@layerswap/wallet-svm"
import { BitcoinProvider } from "@layerswap/wallet-bitcoin"
import useCustomStarknet from "../hooks/useCustomStarknet";
import { WalletProvider } from "@layerswap/widget/types";
import "@layerswap/widget/index.css"

const PageComponent = () => {

    const starknetProvider: WalletProvider = {
        ...StarknetProvider,
        walletConnectionProvider: useCustomStarknet
    }

    return (
        <DynamicContextProvider
            settings={{
                environmentId: {DYNAMIC_ENVIRONMENT_ID},
                walletConnectors: [StarknetWalletConnectors],
            }}
        >
            <LayerswapProvider
                config={{
                    apiKey: {LAYERSWAP_API_KEY},
                    version: 'mainnet',
                    initialValues: {
                        to: 'STARKNET_MAINNET',
                        toAsset: 'USDC',
                        lockTo: true
                    },
                }}
                walletProviders={[EVMProvider, starknetProvider, SVMProvider, BitcoinProvider]}
            >
                <Swap />
            </LayerswapProvider>
        </DynamicContextProvider>
    )
}
For a complete list of configuration options, see Configurations.

Testing

Testnet Configuration

Test your integration on testnet before going live:
const config = {
  apiKey: 'YOUR_TESTNET_API_KEY',
  version: 'testnet',
  initialValues: {
    to: 'STARKNET_SEPOLIA',
    toAsset: 'USDC'
  },
}