Referrals in your UI

Referrals have two halves. This page is the client half you build into your game UI: capturing an invite code when a referred player arrives, attaching it to their login, and showing a player their own code and share links so they can invite others. The payout logic — who earns what, and when — is the server half; it lives in Referrals (server) and you never touch it from the client.

First-touch attribution, so capturing is safe
Attribution is first-touch and server-side: the server records the referrer the first time a code reaches an un-attributed user, and ignores every code after that. Which means you can capture and carry a code on every login without worrying about clobbering an existing referrer — re-attaching is a no-op.

Capturing the code

A referred player lands on a link like https://yourgame.com/?ref=ABC123 (or ?referral=, or a Telegram startapp parameter). The provider captures that automatically on mount — if you’ve mounted IAMGameWalletProvider, the code is already stored and will ride along on the next login. For the common case you write no capture code at all.

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

// The provider captures ?ref= / ?referral= / Telegram startapp on mount.
// Nothing else to do — the captured code is attached to the next login.
export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <IAMGameWalletProvider publishableKey={process.env.NEXT_PUBLIC_WALLET_PK!}>
      {children}
    </IAMGameWalletProvider>
  );
}

When you route the code yourself

If your app owns its own routing and the code doesn’t sit in a plain ?ref= query — say it’s a path segment, or you fetched it from your own backend — hand it to the SDK with setReferralCode. You can also call captureReferralCodeFromUrl manually (e.g. after a client-side navigation the provider didn’t see), and read what’s pending with getPendingReferralCode:

TS
import {
  setReferralCode,
  captureReferralCodeFromUrl,
  getPendingReferralCode,
} from "@iamgame/wallet-sdk";

// You pulled the code off your own routing (path segment, custom param, backend, …):
setReferralCode("ABC123");

// Or re-scan the current URL yourself after a client-side navigation:
const captured = captureReferralCodeFromUrl(); // returns the code, or the stored one

// Inspect what's waiting to be attached to the next login:
const pending = getPendingReferralCode(); // "ABC123" | null
Codes survive the login redirect
The pending code is persisted in localStorage (solven.referralCode), so it survives the login redirect and a Telegram Mini App relaunch. It’s normalised to uppercase and cleared automatically once a login consumes it — you don’t manage its lifecycle.

Attaching the code to a login

There’s nothing extra to wire. Whatever login path the player takes, the SDK attaches the pending code to that login for you and clears it afterward. Your job is just to make sure the code was captured before the player logs in — which the auto-capture on provider mount already guarantees for URL-borne codes. If you’re routing a code manually, call setReferralCode before triggering login.

Showing the player their own code & share link

Once a player is signed in, give them a way to invite others. Two surfaces, depending on how much control you want.

Drop-in share link

WalletShareLink renders the signed-in player’s invite link for the platform the app is running on — it auto-detects web / Telegram / MeWe and picks the right link, with the player’s code already substituted, plus a copy button. It renders nothing while the player is anonymous or if the app has no link templates configured.

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

export function InvitePanel() {
  return <WalletShareLink label="Invite friends & earn" />;
}

Headless: useShareLink

For custom UI, useShareLink() gives you the same data without markup — the best link for the current platform, which platform it targets, and all configured links if you want to render several (a “share to X” vs “share to Telegram” row):

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

export function CustomInvite() {
  const { url, platform, all, loading } = useShareLink();

  if (loading) return <span>Loading your link…</span>;
  if (!url) return null; // anonymous, or no links configured

  return (
    <div>
      <p>Your invite link ({platform}):</p>
      <code>{url}</code>
      <button onClick={() => navigator.clipboard.writeText(url)}>Copy</button>

      {/* Optionally render every platform's link */}
      {all.map((l) => (
        <a key={l.platform} href={l.url}>{l.platform}</a>
      ))}
    </div>
  );
}

The raw code & referral summary

If you want the player’s bare code (to render your own share buttons), or a summary of how their referrals are doing, call getMyReferral() on the wallet client instance you constructed at setup (see Client SDK setup). It returns the player’s code, their referrerUserId (who invited them, if anyone), a directReferrals count, and their referral-earnings balances per currency:

TS
import type { IAMGameWalletClient } from "@iamgame/wallet-sdk";

async function loadReferral(client: IAMGameWalletClient) {
  const me = await client.getMyReferral();
  // {
  //   code: "ABC123",
  //   referrerUserId: "u_… | null",
  //   directReferrals: 12,
  //   balances: [{ currency: 2, balance: "5000000" }],  // base units
  // }
  return me;
}
Balances are base-unit strings
balances[].balance is an integer string in the token’s smallest unit — format it for display with formatTokenAmount, never parseFloat. See Core concepts → Amounts are integers.

Client responsibilities, at a glance

You do (client)The server does
Capture ?ref= / route a code with setReferralCodeRecord first-touch attribution
Let the SDK attach the code to loginBuild the multi-level referral chain
Show the player their code + share linksCalculate and pay out referral earnings

Where to go next