Setup

Everything on the client runs through one component: the provider. You wrap your app in IAMGameWalletProvider once, give it your publishable key, and every wallet hook and component below it just works. This page gets that mounted correctly and explains what you get in return.

New to the mental model (apps, keys, environments, the custodial wallet)? Read Core concepts first — this page assumes you know what a publishable key is.

1. Install

One package covers the provider, hooks, and prebuilt components.

bash
npm install @iamgame/wallet-sdk

Requires React 18+. The SDK is client-side — see the SSR note below.

2. Store your publishable key

The publishable key (pk_test_… / pk_live_…) ships in your client bundle, so it belongs in a public env var. In Next.js that means the NEXT_PUBLIC_ prefix, otherwise the value isn’t exposed to the browser.

bash
# .env.local  (never commit real keys)
NEXT_PUBLIC_IAMGAME_WALLET_PK=pk_test_your_key_here
Publishable only — never the secret key
A pk_ can only act for the wallet of whoever is currently signed in, so it’s safe in the browser. Your sk_ secret key must never appear in client code or a NEXT_PUBLIC_ var — it stays on your backend.

3. Wrap your app in the provider

Point the SDK at the API with baseUrl and pass your key. Mount it high enough that every part of your game that touches the wallet is inside it — typically your root layout.

TS
"use client";

import { IAMGameWalletProvider } from "@iamgame/wallet-sdk";

export function WalletProviders({ children }: { children: React.ReactNode }) {
  return (
    <IAMGameWalletProvider
      publishableKey={process.env.NEXT_PUBLIC_IAMGAME_WALLET_PK!}
      baseUrl="https://api-wallet.iamgame.com/v1"
      onError={(err, { operation }) => {
        // Wire this to your error tracker (Sentry, etc.).
        console.error("[wallet]", operation, err);
      }}
    >
      {children}
    </IAMGameWalletProvider>
  );
}

The three props you’ll actually set:

PropRequiredWhat it does
publishableKeyYesYour app's pk_. Picks the environment (test → devnet, live → mainnet) and identifies your app.
baseUrlYesThe wallet API. Use https://api-wallet.iamgame.com/v1 for both test and live — the key decides the environment, not the URL.
onErrorOptionalCalled when any wallet operation fails in the UI (login, sign, withdraw, export, wallet load, or a render crash). Wire it to your error tracker; omit it and failures just aren't reported.
autoTelegram is on by default
Inside a Telegram Mini App the provider signs the player in automatically from Telegram’s initData — no button, no modal. Pass autoTelegram={false} to require an explicit login instead. Outside Telegram it’s a silent no-op. More in Login & sign-in.

What the provider gives you

Once mounted, everything below it can read the auth state and the signed-in player’s wallet through hooks and components — no prop-drilling, no manual token passing:

  • Auth state & sign-in methods via useWalletAuth and the prebuilt WalletLogin / WalletLoginModal.
  • The wallet, address & balances via useWallet, useWalletBalance and WalletAddress / WalletBalance.
  • Signing, deposit, withdraw & export via useWalletSign, WalletDeposit, WalletWithdraw, WalletExport.

Session persistence

On a successful login the provider stores a session and, by default, persists it in localStorage so the player stays signed in across reloads. On mount it bootstraps that stored session, silently refreshing the access token if it has expired. Your auth status moves through loading → anonymous → authenticated as this settles.

Need a non-persistent session (e.g. an embedded/kiosk context)? Swap the storage strategy — both are exported:

TS
import { IAMGameWalletProvider, inMemorySession } from "@iamgame/wallet-sdk";

<IAMGameWalletProvider
  publishableKey={process.env.NEXT_PUBLIC_IAMGAME_WALLET_PK!}
  baseUrl="https://api-wallet.iamgame.com/v1"
  storage={inMemorySession()}   // default is localStorageSession() — persists across reloads
>
  {children}
</IAMGameWalletProvider>

The client session token is short-lived and is not proof of identity on its own — your backend must verify it server-to-server. That handoff lives in Authentication.

SSR & “use client”

The provider, hooks, and components use React state and browser APIs (localStorage, window.Telegram), so they only run in the browser. In the Next.js App Router, any file that mounts the provider or calls a wallet hook needs the “use client” directive at the top.

  • Put the provider in a small client component (like WalletProviders above) and render that from your server layout.tsx — the layout itself stays a server component.
  • Anything calling useWalletAuth, useWallet, useWalletBalance, etc. must also be a client component.
TS
// app/layout.tsx  (server component — no "use client")
import { WalletProviders } from "./WalletProviders";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <WalletProviders>{children}</WalletProviders>
      </body>
    </html>
  );
}
Telegram detection is hydration-safe
Helpers like useIsTelegram() return false on the server and the first client render, then the real value after mount — so they never cause a hydration mismatch. You don’t need to guard them yourself.

Where to go next