Withdraw & export
Two exits for a player’s funds: withdraw sends some tokens out to any Solana address (cash-out to an exchange, pay a friend), while export hands the player the private key to their whole wallet so they can walk away into full self-custody. Both are drop-in components — mount them under the wallet provider and they wire themselves to the signed-in player.
WalletWithdraw
A complete send form: token picker, amount input with a MAX button, address and balance validation, a confirm button, and the on-chain transaction signature on success. It reads the player’s balances itself and only submits once every check passes.
import { WalletWithdraw } from "@iamgame/wallet-sdk";
export function WithdrawPanel() {
return (
<WalletWithdraw
onSuccess={(signature) => {
console.log("sent, tx:", signature);
}}
onError={(err) => {
console.error("withdraw failed:", err.message);
}}
/>
);
}It renders nothing while the player is signed out. Once signed in it shows their tokens and available balance automatically.
Props
| Prop | Type | What it does |
|---|---|---|
mint | string | Restrict to a single token. Omit to let the player pick SOL or any SPL token they hold. |
onSuccess | (signature: string) => void | Called with the confirmed tx signature after a successful send. |
onError | (error: Error) => void | Called on validation or send failure (see the note below). |
label | string | Header text. Default "Withdraw". |
theme | WalletWithdrawTheme | Colour / radius / font overrides. |
To lock the form to one currency — say a USDC-only cash-out — pass mint. The token picker disappears and the form works entirely in that token:
<WalletWithdraw
mint="EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" // USDC
label="Cash out USDC"
onSuccess={(sig) => toast(`Sent — ${sig.slice(0, 8)}…`)}
/>What the component handles for you
- Address validation — rejects anything that isn’t a valid base58 Solana pubkey before submitting.
- Amount + balance checks — parses the decimal amount into base units and blocks a send that exceeds the available balance.
- MAX with a fee reserve — for SOL, MAX leaves ~0.005 SOL behind so the network fee can be paid; for SPL tokens MAX is the full balance.
- Fee sponsorship — the send goes through the signer, which sponsors the network fee where applicable, so a player with USDC but no SOL can still withdraw.
- Double-submit guard + idempotency — a fresh idempotency key per submission and a hard busy-guard mean a double-tap never sends twice.
status: "failed" when the on-chain send didn’t land (e.g. no SOL for the network fee). The component treats that as a failure — it shows the error and calls onError, not onSuccess. If you’re building custom UI over the client instead of using the component, check result.status yourself; don’t assume a resolved promise means the funds moved.Theming
Same token set as the other components, plus a danger colour for the error state:
<WalletWithdraw
theme={{
background: "#0d0e11",
foreground: "#f5f5f4",
muted: "#8a8577",
surface: "#16181d",
border: "#26282e",
primary: "#d4a537", // submit + MAX buttons
danger: "#ef4444", // error banner
radius: "0.75rem",
}}
/>WalletExport — self-custody
The IAMGame wallet is custodial by default — the key lives in secure hardware on the signer, not in the browser (see Core concepts). Export is the escape hatch: it reveals the wallet’s private key to the player once, deletes that key from our servers, and provisions the player a fresh custodial wallet for future play — with the remaining balance transferred over automatically. After export the player fully owns the old key; they can import it into any Solana wallet.
import { WalletExport } from "@iamgame/wallet-sdk";
export function ExportPanel() {
return (
<WalletExport
onSuccess={(newWalletAddress) => {
console.log("player exported; new wallet:", newWalletAddress);
}}
onError={(err) => console.error("export failed:", err.message)}
/>
);
}The flow the component runs
WalletExport is a guided three-step flow — you don’t wire any of it:
| Step | What the player sees |
|---|---|
| Preflight | An eligibility check: current balance vs the minimum required, and the amount bootstrapped to the new wallet. If the balance is too low the button is disabled with a reason and a Re-check button. |
| Confirm | A hard “are you sure?” warning — the old key is revealed then deleted, a new wallet is created. |
| Revealed | The private key shown exactly once with a copy button, plus the new wallet address and the bootstrap tx signature. |
currentBalanceLamports, minBalanceLamports, bootstrapLamports, allowed, reason) come straight from the server preflight.Props
| Prop | Type | What it does |
|---|---|---|
onSuccess | (newWalletAddress: string) => void | Called after a successful export with the address of the freshly provisioned wallet. |
onError | (error: Error) => void | Called if the export fails. |
label | string | Header text. Default "Export Wallet". |
theme | WalletExportTheme | Colour / radius / font overrides (includes a danger colour for the warnings). |
Where to go next
- Deposit & on-ramp — the other side: getting funds in.
- Core concepts — the custodial wallet model and why export exists.
- Custodial treasury — the server side of real-money withdrawals.