@orderly.network/react-app is an application layer of the Orderly API. It is mainly responsible for rendering the UI, handling user interaction events, and completing trading actions. Therefore, it does not include built-in wallet connection functionality.

In places where wallet connection needs to be handled, @orderly.network/react-app utilizes React’s context mechanism to obtain information about the currently connected wallet or call wallet connection functions provided by the context. To facilitate this, @orderly.network/react-app defines a context named WalletConnectorContext. Any component that implements the WalletConnectorContext.Provider defined in WalletConnectorContextState can serve as the wallet connection component for the app. Please refer to this link for more details.

Using the wallet connection component provided by Orderly

Orderly SDK provides a built-in wallet connection component supporting both EVM and Solana. Here’s how to integrate it:

Install Wallet Connector Dependencies

npm install @orderly.network/wallet-connector

Add the WalletConnectorProvider Component

Wrap your application with the WalletConnectorProvider for wallet connection functionality:

import { WalletConnectorProvider } from "@orderly.network/wallet-connector";
import { WalletAdapterNetwork } from "@solana/wallet-adapter-base";

const App: FC<{ children: ReactNode }> = (props) => {
  return (
    <WalletConnectorProvider solanaInitial={{network : WalletAdapterNetwork.Mainnet}}>
      <OrderlyAppProvider brokerId="orderly" brokerName="Orderly" networkId="testnet" appIcons={""}>
        {props.children}
      </OrderlyAppProvider>
    </WalletConnectorProvider>
  );
};

Wallet Connector Privy

The Privy wallet connector consists of three parts:

  • Privy: Provides social login, featuring an injected wallet, EVM wallets, and Solana wallets.
  • Wagmi: Offers connectivity for EVM wallets, such as MetaMask and WalletConnect.
  • Solana: Provides connectivity for Solana wallets, such as Phantom and Ledger.

Configuration options

  • network (required) – e.g., mainnet or testnet. This determines the network used by wallets like Solana and Abstract.
  • customChains (optional) – Brokers can define which chains to display.
  • termsOfUse (optional) – URL for Terms of Use; if not provided, terms section won’t be shown.
interface WalletConnectorPrivyProps{
  network: Network;
  customChains?: Chains;
  termsOfUse?: string;
  privyConfig?: InitPrivy;
  wagmiConfig?: InitWagmi;
  solanaConfig?: InitSolana;
}
  • If privyConfig is not configured, the Privy connector will be disabled.
  • If wagmiConfig is not configured, the Wagmi connector will be disabled.
  • If solanaConfig is not configured, the Solana connector will be disabled.
  • If abstractConfig is not configured, the Abstract Global Wallet connector will be disabled.
  • At least one of privyConfig, wagmiConfig, or solanaConfig must be provided.

Behavior based on customChains:

  • If only Solana chains are included, Privy will show only the Solana injected wallet and disable the Wagmi connector.
  • If only EVM chains are included, Privy will show only the EVM injected wallet and disable the Solana connector.

Example

import WalletConnectorPrivyProvider from "@orderly.network/wallet-connector-privy";

<WalletConnectorPrivyProvider
  termsOfUse="https://learn.woo.org/legal/terms-of-use"
  network={Network.testnet}
  privyConfig={{
    appid: "your privy appid",
    config: {
      appearance: {
        theme: "dark",
        accentColor: "#181C23",
        logo: "/orderly-logo.svg",
      },
    },
  }}
  wagmiConfig={{
    connectors: [
      wagmiConnectors.injected(),
      wagmiConnectors.walletConnect({
        projectId: "your project id",
        showQrModal: true,
        storageOptions: {},
        metadata: {
          name: "Orderly Network",
          description: "Orderly Network",
          url: "https://orderly.network",
          icons: ["https://oss.orderly.network/static/sdk/chains.png"],
        },
      }),
    ],
  }}
  solanaConfig={{
    mainnetRpc: "",
    devnetRpc: "https://api.devnet.solana.com",
    wallets: wallets,
    onError: (error: WalletError, adapter?: Adapter) => {
      console.log("-- error", error, adapter);
    },
  }}
  abstractConfig={{}}
>
  <OrderlyAppProvider>
    {props.children}
  </OrderlyAppProvider>
</WalletConnectorPrivyProvider>

Custom wallet connection provider

You can also customize wallet connections by implementing all the properties defined in WalletConnectorContextState. Define the component in the following format:

import { PropsWithChildren } from "react";
import { WalletConnectorContext } from "@orderly.network/hooks";

export const WalletConnector = ({ children }: PropsWithChildren) => {
  return (
    <WalletConnectorContext.Provider
      value={{
        /*
         * implement all properties defined in WalletConnectorContextState
         */
      }}
    >
      {children}
    </WalletConnectorContext.Provider>
  );
};

After defining the component, you also need to add this component to the parent of OrderlyAppProvider.

Interface & Context

export type ConnectedChain = {
  id: string;
};

export interface WalletConnectorContextState {
  connect: () => Promise<any[]>;
  disconnect: (options: any) => Promise<any[]>;
  connecting: boolean;
  setChain: (options: any) => Promise<any>;
  chains: any[];
  switchChain: (options: { chainId: string }) => Promise<any>;
  wallet: any;
  connectedChain: ConnectedChain | null;
  settingChain: boolean;
}

export const WalletConnectorContext = createContext<WalletConnectorContextState>(
  {} as WalletConnectorContextState
);

export const useWalletConnector = () => {
  return useContext(WalletConnectorContext);
};

Implementation

The following example demonstrates a wallet connection component implemented using web3modal. You can use this example as a reference to implement your own wallet connection component.

import { WalletConnectorContext, WalletState } from "@orderly.network/hooks";
import { ChainNamespace } from "@orderly.network/types";
import { createWeb3Modal, defaultWagmiConfig, useWeb3Modal } from "@web3modal/wagmi/react";
import { FC, PropsWithChildren, useEffect, useState } from "react";
import { useAccount as useWagmiAccount } from "wagmi";
import { arbitrum, mainnet } from "wagmi/chains";

// 1. Get projectId at https://cloud.reown.com/
const projectId = "YOUR_PROJECT_ID";

// 2. Create wagmiConfig
const metadata = {
  name: "Web3Modal",
  description: "Web3Modal Example",
  url: "https://web3modal.com",
  icons: ["https://avatars.githubusercontent.com/u/37784886"]
};

const chains = [mainnet, arbitrum];
export const wagmiConfig = defaultWagmiConfig({ chains, projectId, metadata });

// 3. Create modal
createWeb3Modal({ wagmiConfig, projectId });

export const OrderlyProvider: FC<PropsWithChildren> = ({ children }) => {
  const [wallet, setWallet] = useState<WalletState>({
    chains: chains.map((chain) => ({
      namespace: ChainNamespace.evm,
      id: chain.id
    })),
    accounts: [],
    icon: "",
    label: "",
    provider: null
  });

  const { open } = useWeb3Modal();
  const { address, isConnecting, chain, connector, status } = useWagmiAccount();

  useEffect(() => {
    const run = async () => {
      if (!connector) return;
      setWallet({
        ...wallet,
        accounts: await connector.getAccounts().then((addresses) =>
          addresses.map((addr) => ({
            address: addr
          }))
        ),
        provider: await connector.getProvider(),
        label: (await connector.getClient?.().then((client) => client.name)) ?? ""
      });
    };
    run();
  }, [address, wallet, setWallet, connector]);

  return (
    <WalletConnectorContext.Provider
      value={{
        connect: () => {
          return open().then(() => []);
        },
        disconnect: async () => {
          connector?.disconnect();
          return [];
        },
        setChain: async ({ chainId }) => {
          return connector?.switchChain?.({ chainId: Number(chainId) });
        },
        chains,
        connectedChain: chain ? { id: chain.id, namespace: ChainNamespace.evm } : null,
        namespace: ChainNamespace.evm,
        connecting: isConnecting,
        settingChain: status === "reconnecting",
        wallet
      }}
    >
      {children}
    </WalletConnectorContext.Provider>
  );
};