Build
Frontend Tutorials
UniversalKit

UniversalKit by ZetaChain provides a set of React components designed to make building universal applications easier.

The components are expected to work with Next.js (opens in a new tab), RainbowKit (opens in a new tab) and wagmi (opens in a new tab).

Start from a template (recommended):

git clone https://github.com/zeta-chain/template
cd template/web
yarn

Or add UniversalKit to your existing app:

yarn add @zetachain/universalkit
The component library is in under active development and currently is recommended for testnet use only.

UniversalKit Provider is a React context provider that initializes and provides shared state between components.

To initialize the provider, wrap your application with the UniversalKitProvider:

src/app/providers.tsx
import { UniversalKitProvider } from "@zetachain/universalkit";
 
<UniversalKitProvider>{children}</UniversalKitProvider>;

If you're using the template (opens in a new tab), the provider is already initialized.

ZetaChain Client is a client that provides access to useful methods for interacting with ZetaChain. The client is powered by the Toolkit (opens in a new tab) library.

Some of the components require a ZetaChain Client to be passed as a prop. To initialize the client, use the useZetaChainClient hook:

src/app/page.tsx
import { useZetaChainClient } from "@zetachain/universalkit";
 
const client = useZetaChainClient({ network: "testnet", signer });

Instantiating a client manually allows you to configure it, for example, specifying a different RPC endpoint:

const client = useZetaChainClient({
  signer,
  network: "testnet",
  chains: {
    zeta_testnet: {
      api: [
        {
          url: `https://zetachain-athens.g.allthatnode.com/archive/evm/${process.env.KEY}`,
          type: "evm",
        },
      ],
    },
  },
});

Bitcoin Wallet Provider is a React context provider that initializes and provides functionality for connecting a Bitcoin wallet to the application: address of the connected Bitcoin wallet, connection status, connected wallet type, method to connect a wallet, method to disconnect a wallet, and method to send a transaction.

import { useBitcoinWallet } from "@zetachain/universalkit";
 
const { address, loading, connectedWalletType, connectWallet, disconnect, sendTransaction } = useBitcoinWallet();

Connect Bitcoin is a component that allows users to connect their Bitcoin wallet to the application. Currently, OKX, XDEFI and UniSat wallets are supported.

After a Bitcoin wallet is connected, the current address becomes available from the BitcoinWalletProvider.

src/app/page.tsx
"use client";
 
import { ConnectButton } from "@rainbow-me/rainbowkit";
import { ConnectBitcoin } from "@zetachain/universalkit";
 
const Page = () => {
  return (
    <div className="m-4">
      <div className="flex justify-end gap-2 mb-10">
        <ConnectBitcoin />
        <ConnectButton label="Connect EVM" showBalance={false} />
      </div>
      <div className="flex justify-center">
        <div className="w-[400px]">{/* Add components here */}</div>
      </div>
    </div>
  );
};
 
export default Page;

The Swap component provides the following functionality:

  • Swap between connected chains (for example, Ethereum and BNB)
  • Deposit native gas and supported ERC-20 tokens from a connected chain to ZetaChain
  • Withdraw ZRC-20 tokens from ZetaChain to a connected chain
  • Send ZETA between chains
  • Transfer native gas and ERC-20 tokens on connected chains

For cross-chain swaps the component depends on a universal swap contract. Complete the swap tutorial and deploy your own contract, or use the contract address from the example below. contract or use the contract address from the example below.

src/app/page.tsx
"use client";
 
import { ConnectButton } from "@rainbow-me/rainbowkit";
import { Swap, useEthersSigner, useZetaChainClient, ConnectBitcoin, useBitcoinWallet } from "@zetachain/universalkit";
import { useAccount, useChainId, useWalletClient } from "wagmi";
 
const contract = "0xb459F14260D1dc6484CE56EB0826be317171e91F"; // universal swap contract
 
const Page = () => {
  const account = useAccount();
  const chainId = useChainId();
  const { data: walletClient } = useWalletClient({ chainId });
  const signer = useEthersSigner({ walletClient });
  const client = useZetaChainClient({ network: "testnet", signer });
  const { address: bitcoinAddress } = useBitcoinWallet();
 
  return (
    <div className="m-4">
      <div className="flex justify-end gap-2 mb-10">
        <ConnectBitcoin />
        <ConnectButton label="Connect EVM" showBalance={false} />
      </div>
      <div className="flex justify-center">
        <div className="w-[400px]">
          {client && <Swap contract={contract} client={client} account={account} bitcoin={bitcoinAddress} />}
        </div>
      </div>
    </div>
  );
};
 
export default Page;

The Balances component provides a list of token balances on all chains connected to ZetaChain, with the ability to search the list and filter tokens by chain.

src/app/page.tsx
"use client";
 
import { ConnectButton } from "@rainbow-me/rainbowkit";
import {
  Balances,
  useEthersSigner,
  useZetaChainClient,
  ConnectBitcoin,
  useBitcoinWallet,
} from "@zetachain/universalkit";
import { useAccount, useChainId, useWalletClient } from "wagmi";
 
const Page = () => {
  const account = useAccount();
  const chainId = useChainId();
  const { data: walletClient } = useWalletClient({ chainId });
  const signer = useEthersSigner({ walletClient });
  const client = useZetaChainClient({ network: "testnet", signer });
  const { address: bitcoinAddress } = useBitcoinWallet();
 
  return (
    <div className="m-4">
      <div className="flex justify-end gap-2 mb-10">
        <ConnectBitcoin />
        <ConnectButton label="Connect EVM" showBalance={false} />
      </div>
      <div className="flex justify-center">
        <div className="w-[400px]">
          {client && <Balances client={client} account={account} bitcoin={bitcoinAddress} />}
        </div>
      </div>
    </div>
  );
};
 
export default Page;

The Profile component resolves an address to a domain name and displays it as a button. For resolving addresses the component uses Space ID Web3 Name SDK (opens in a new tab).

src/app/page.tsx
"use client";
 
import { ConnectButton } from "@rainbow-me/rainbowkit";
import { Profile, ConnectBitcoin } from "@zetachain/universalkit";
import { useAccount } from "wagmi";
 
const Page = () => {
  const account = useAccount();
  return (
    <div className="m-4">
      <div className="flex justify-end gap-2 mb-10">
        <ConnectBitcoin />
        <ConnectButton label="Connect EVM" showBalance={false} />
      </div>
      <div className="flex justify-center">
        <div className="w-[400px]">
          <Profile address={account.address} />
        </div>
      </div>
    </div>
  );
};
 
export default Page;

The Staking Rewards component displays the staked amount, number of tokens being unstaked, and the number of rewards earned. The component also allows users to claim staking rewards.

src/app/page.tsx
"use client";
 
import { ConnectButton } from "@rainbow-me/rainbowkit";
import {
  StakingRewards,
  useEthersSigner,
  useZetaChainClient,
  ConnectBitcoin,
  useBitcoinWallet,
} from "@zetachain/universalkit";
import { useAccount, useChainId, useWalletClient } from "wagmi";
 
const Page = () => {
  const account = useAccount();
  const chainId = useChainId();
  const { data: walletClient } = useWalletClient({ chainId });
  const signer = useEthersSigner({ walletClient });
  const client = useZetaChainClient({ network: "testnet", signer });
  const { address: bitcoinAddress } = useBitcoinWallet();
 
  return (
    <div className="m-4">
      <div className="flex justify-end gap-2 mb-10">
        <ConnectBitcoin />
        <ConnectButton label="Connect EVM" showBalance={false} />
      </div>
      <div className="flex justify-center">
        <div className="w-[400px]">{client && <StakingRewards client={client} account={account} />}</div>
      </div>
    </div>
  );
};
 
export default Page;