Initialize Nexus SDK using RainbowKit
WANT TO SKIP THE TUTORIAL?
We created two template repos for devs to easily get started with the Nexus SDK using Next JS and Vite.
We recommend going through the tutorial first, but to each their own:
Prerequisites
The following tutorial uses pnpm
as the package manager, adjust the commands
if you’re using a different one.
-
Make sure Node.js is installed on your system. We recommend version
20.x.x
or higher. -
Set up a package manager of your choice. We use pnpm for this tutorial, but alternatives like npm and yarn work just fine.
-
Make sure you have an EIP-1193 compatible wallet provider (for example, MetaMask) installed in your browser to follow along with the tutorial.
Objectives
By the end of this tutorial you will have:
- Understood how to initialize the Nexus SDK inside a frontend environment.
- Implemented said initialization using Next JS with RainbowKit.
- Fetched unified balances using the Nexus SDK.
WHAT IS A ‘UNIFIED BALANCE’?
You can refer to this page in our docs for more details.
What Does ‘Initialization’ Mean Here?
-
The Nexus SDK allows you to seamlessly transfer tokens across-chains using the
.transfer()
and.bridge()
functions (We’ll go through them in the following tutorials). You can also fetch unified balances using the.getUnifiedBalances()
function. -
To do any of that though, the SDK needs access to a wallet. You can do this on the frontend by using an injected wallet provider that follows the
EIP-1193
standard. -
And to do that, you need to call the
.initialize()
function of the SDK while passing the wallet provider to it. -
The flows to do this differ between the headless and the widgets SDK. We will go over both of them in this tutorial.
The .initialize()
function must be called AFTER a wallet has been connected.
Otherwise, the SDK will throw an error.
Initialize the Nexus SDK using Next JS with RainbowKit
Objective
In this section we will create a minimal Next JS page with 4 buttons:
- The first button will be used to connect a wallet to the app using RainbowKit.
- The second button will be used to initialize the SDK using
nexus-core
. - The third button will be used to fetch the unified balances using the Nexus SDK.
- The fourth button will de-initialize the SDK using the
deinit()
function.
Set up a basic Next JS project
- Navigate into a directory of your choice and create a new Next JS project at it’s root:
pnpm create next-app@latest . --ts --eslint --app --src-dir
- Install the required packages:
pnpm add @avail-project/nexus-core@0.0.3-beta.0 @rainbow-me/rainbowkit wagmi viem @tanstack/react-query
- Update the
src/app/page.tsx
file to the following:
export default function Home() {
return <div>Hello Nexus!</div>;
}
- Run a dev server using:
pnpm dev
You should now have a bare bones Next JS project running in your browser.
Let us now get down to business!!!
Set up RainbowKit
- Create a file at
src/lib/wagmi.ts
and add the following configuration:
import { getDefaultConfig } from '@rainbow-me/rainbowkit';
import { mainnet, arbitrum, polygon, optimism, base, avalanche } from 'wagmi/chains';
export const config = getDefaultConfig({
appName: 'Nexus SDK with RainbowKit',
projectId: 'YOUR_PROJECT_ID', // Get this from https://cloud.walletconnect.com/
chains: [mainnet, arbitrum, polygon, optimism, base, avalanche],
ssr: true, // If your dApp uses server side rendering (SSR)
});
You need to get a Project ID from WalletConnect Cloud for RainbowKit to work properly. This is free and only takes a few minutes to set up.
- Create a file at
src/components/providers.tsx
and add the RainbowKit providers:
'use client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { WagmiProvider } from 'wagmi';
import { RainbowKitProvider } from '@rainbow-me/rainbowkit';
import { config } from '@/lib/wagmi';
import '@rainbow-me/rainbowkit/styles.css';
const queryClient = new QueryClient();
export function Providers({ children }: { children: React.ReactNode }) {
return (
<WagmiProvider config={config}>
<QueryClientProvider client={queryClient}>
<RainbowKitProvider>
{children}
</RainbowKitProvider>
</QueryClientProvider>
</WagmiProvider>
);
}
- Update the
src/app/layout.tsx
file to wrap your app with the providers:
import { Providers } from '@/components/providers';
import './globals.css';
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<Providers>
{children}
</Providers>
</body>
</html>
);
}
Set up an instance of the Nexus SDK
We will set up a single instance of the Nexus SDK and use it across the app. This will help keep the code clean and make it easier to understand the flow of the app.
- Create a file at
src/lib/nexus.ts
and add the following code:
import { NexusSDK } from '@avail-project/nexus-core';
export const sdk = new NexusSDK({ network: 'testnet'});
This initializes the Nexus SDK with the testnet
chains.
If this param is not provided, the SDK will default to mainnet
.
You can check out a complete list of the supported networks here.
- Now fill the page with the following helper functions:
// Thin wrapper that calls sdk.isInitialized() from the SDK
export function isInitialized() {
return sdk.isInitialized();
}
export async function initializeWithProvider(provider: any) {
if (!provider) throw new Error('No EIP-1193 provider (e.g., MetaMask) found');
//If the SDK is already initialized, return
if (sdk.isInitialized()) return;
//If the SDK is not initialized, initialize it with the provider passed as a parameter
await sdk.initialize(provider);
}
export async function deinit() {
//If the SDK is not initialized, return
if (!sdk.isInitialized()) return;
//If the SDK is initialized, de-initialize it
await sdk.deinit();
}
export async function getUnifiedBalances() {
//Get the unified balances from the SDK
return await sdk.getUnifiedBalances();
}
Let’s quickly go over the role of each function:
isInitialized()
: This thin wrapper callssdk.isInitialized()
from the Nexus SDK to query and return the initialization status of the SDK.initializeWithProvider(provider: any)
: This function checks if the SDK is already initialized and if not, it initializes it with the provider passed as a parameter.
This function must be called AFTER a wallet has been connected. Otherwise, the SDK will throw an error.
deinit()
: This function checks if the SDK is initialized and if so, it de-initializes it.getUnifiedBalances()
: This function callssdk.getUnifiedBalances()
from the Nexus SDK to fetch the unified balance of the user.
Set up the buttons
Create four new files in the src/components
directory:
connect-button.tsx
This component will be used to connect a wallet using RainbowKit.
'use client';
import { ConnectButton } from '@rainbow-me/rainbowkit';
export default function ConnectWalletButton({ className }: { className?: string }) {
return (
<ConnectButton.Custom>
{({
account,
chain,
openAccountModal,
openChainModal,
openConnectModal,
authenticationStatus,
mounted,
}) => {
const ready = mounted && authenticationStatus !== 'loading';
const connected =
ready &&
account &&
chain &&
(!authenticationStatus ||
authenticationStatus === 'authenticated');
return (
<div
{...(!ready && {
'aria-hidden': 'true',
'style': {
opacity: 0,
pointerEvents: 'none',
userSelect: 'none',
},
})}
>
{(() => {
if (!connected) {
return (
<button className={className} onClick={openConnectModal} type="button">
Connect Wallet
</button>
);
}
if (chain.unsupported) {
return (
<button className={className} onClick={openChainModal} type="button">
Wrong network
</button>
);
}
return (
<div style={{ display: 'flex', gap: 12 }}>
<button
className={className}
onClick={openChainModal}
style={{ display: 'flex', alignItems: 'center' }}
type="button"
>
{chain.hasIcon && (
<div
style={{
background: chain.iconBackground,
width: 12,
height: 12,
borderRadius: 999,
overflow: 'hidden',
marginRight: 4,
}}
>
{chain.iconUrl && (
<img
alt={chain.name ?? 'Chain icon'}
src={chain.iconUrl}
style={{ width: 12, height: 12 }}
/>
)}
</div>
)}
{chain.name}
</button>
<button className={className} onClick={openAccountModal} type="button">
{account.displayName}
{account.displayBalance
? ` (${account.displayBalance})`
: ''}
</button>
</div>
);
})()}
</div>
);
}}
</ConnectButton.Custom>
);
}
init-button.tsx
This component will be used to initialize the SDK using nexus-core
.
'use client';
import { useAccount } from 'wagmi';
import { initializeWithProvider, isInitialized } from '../lib/nexus';
export default function InitButton({
className,
onReady,
}: { className?: string; onReady?: () => void }) {
const { connector } = useAccount();
const onClick = async () => {
try {
// Get the provider from the connected wallet
const provider = await connector?.getProvider();
if (!provider) throw new Error('No provider found');
// We're calling our wrapper function from the lib/nexus.ts file here.
await initializeWithProvider(provider);
onReady?.();
alert('Nexus initialized');
} catch (e: any) {
alert(e?.message ?? 'Init failed');
}
};
return <button className={className} onClick={onClick} disabled={isInitialized()}>Initialize Nexus</button>;
}
fetch-unified-balance-button.tsx
This component will be used to fetch the unified balances using the Nexus SDK.
'use client';
import { getUnifiedBalances, isInitialized } from '../lib/nexus';
export default function FetchUnifiedBalanceButton({
className,
onResult,
}: { className?: string; onResult?: (r: any) => void }) {
const onClick = async () => {
if (!isInitialized()) return alert('Initialize first');
const res = await getUnifiedBalances();
onResult?.(res);
console.log(res);
};
return <button className={className} onClick={onClick} disabled={!isInitialized()}>Fetch Unified Balances</button>;
}
de-init-button.tsx
This component will be used to de-initialize the SDK using the deinit()
function.
'use client';
import { deinit, isInitialized } from '../lib/nexus';
export default function DeinitButton({
className,
onDone,
}: { className?: string; onDone?: () => void }) {
const onClick = async () => {
await deinit();
onDone?.();
alert('Nexus de-initialized');
};
return <button className={className} onClick={onClick} disabled={!isInitialized()}>De-initialize</button>;
}
Set up the Landing Page
Update the src/app/page.tsx
file to the following:
'use client';
import { useState } from 'react';
import { useAccount } from 'wagmi';
import ConnectWalletButton from '@/components/connect-button';
import InitButton from '@/components/init-button';
import FetchUnifiedBalanceButton from '@/components/fetch-unified-balance-button';
import DeinitButton from '@/components/de-init-button';
import { isInitialized } from '@/lib/nexus';
export default function Page() {
const { isConnected } = useAccount();
const [initialized, setInitialized] = useState(isInitialized());
const [balances, setBalances] = useState<any>(null);
const btn =
'px-4 py-2 rounded-md bg-blue-600 text-white hover:bg-blue-700 ' +
'disabled:opacity-50 disabled:cursor-not-allowed';
return (
<main className="min-h-screen flex items-center justify-center">
<div className="flex flex-col items-center gap-4">
<ConnectWalletButton className={btn} />
<InitButton className={btn} onReady={() => setInitialized(true)} />
<FetchUnifiedBalanceButton className={btn} onResult={(r) => setBalances(r)} />
<DeinitButton className={btn} onDone={() => { setInitialized(false); setBalances(null); }} />
<div className="mt-2">
<b>Wallet Status:</b> {isConnected ? 'Connected' : 'Not connected'}
</div>
<div className="mt-2">
<b>Nexus SDK Initialization Status:</b> {initialized ? 'Initialized' : 'Not initialized'}
</div>
{balances && (
<pre className="whitespace-pre-wrap">{JSON.stringify(balances, null, 2)}</pre>
)}
</div>
</main>
);
}
sdk.getUnifiedBalances()
returns a JSON object with the balances of the user on each of the supported chains.
We’re rendering it out on the screen raw to keep the tutorial simple, but you can always format it as you please.
What’s Happening Here?
- We use the
useState
hook to manage the initialization status and balances of the SDK. - We use RainbowKit’s
useAccount
hook to check wallet connection status. - Some basic CSS is applied to the buttons to arrange them in a column.
- Some buttons are conditionally toggled based on the initialization status of the SDK.
- All in all, clicking through the buttons will allow you to initialize the SDK, fetch balances for a user, and de-initialize the SDK.
Think About What You Just Did Here!!!
In just a few lines of code, you leveraged the power of the Nexus SDK to fetch a list of tokens that the user holds across several different chains. You can now move around those tokens as you please, the possibilities are endless!
You might want to bridge one of the tokens across chains, or maybe you want to swap all existing tokens of your user spread across
different chains into ETH
on Arbitrum
.
Once you have a wallet connected and the SDK initialized, you can do all of this and more!