Ledger
Signing an on-chain transaction per game action is too slow (and too expensive) for real-time betting. The ledger fixes that with a seamless-wallet model: lock a stake once, run bet / win / rollback off-chain at memory speed, and settle the net on-chain at the end. The wallet is the money source of truth; your game owns the game meaning.
The shape of a session
import { IAMGameWalletServer } from "@iamgame/wallet-sdk-server";
const wallet = new IAMGameWalletServer({
secretKey: process.env.IAMGAME_WALLET_SECRET_KEY!, // sk_ — BACKEND ONLY
baseUrl: "https://api-wallet.iamgame.com/v1",
});
// 1) Open a session — ONE lock. The funds are held; the session can bet up to it.
const s = await wallet.ledger.openSession({
userId, gameCode: "momentum", currency: "USDC",
lock: { amount: 50_000_000n }, // integer base units (micro-USDC), always
}); // → { sessionToken, lockedBalance }
// 2) Hot path — memory-speed, unlimited frequency. Idempotent on transactionId.
await wallet.ledger.bet({ sessionToken: s.sessionToken,
transactionId: "r1", round: "1", amount: 1_000_000n });
await wallet.ledger.win({ sessionToken: s.sessionToken,
transactionId: "r1-w", referenceTransactionId: "r1",
round: "1", amount: 1_800_000n, roundClosed: true });
// await wallet.ledger.rollback({ sessionToken, referenceTransactionId: "r1" });
// await wallet.ledger.balance({ sessionToken });
// 3) Settle once — the NET result moves back on-chain.
const { payout } = await wallet.ledger.settleSession(s.sessionToken);Contract semantics (get these exact)
Idempotent on transactionId — a duplicate bet/win returns the stored result, never double-debits. Safe to retry. Use your own bet id as the transactionId — it’s also the reconciliation key.win must reference its bet (referenceTransactionId) — a win for an unseen bet is rejected.
Rollback is order-independent — a rollback that arrives before its bet is remembered, and the late bet is voided. Use it to cancel a round.
Integer money only — base units (lamports, micro-USDC). Never floats.
Rounds — every transaction carries a round; the last one sets roundClosed: true, which drives reconciliation.
Server-to-server only
Every ledger call uses your sk_ key and must run on your backend — the browser must never be able to call win on itself. The client SDK has no ledger surface by design; the ledger lives in @iamgame/wallet-sdk-server.
Funds are protected
Funds locked in an open session are untouchable by withdraw or export — a player can’t pull money out from under an active bet. Settlement releases the lock and moves the net result; a loss is simply a bet with no matching win.
Pari-mutuel & pools
The ledger holds per-player money, not the pool. Each player has their own session and their stake is a bet. The pool, the split, the odds, and who won are your game’s domain — compute each winner’s payout, then call win against their bet. Your records and the ledger reconcile on the shared transactionId.
Environments & settlement
Build and test the whole flow in test (devnet) — it runs entirely off-chain. Real-money (live) sessions settle through an on-chain rail (a PDA asset-lock); until that rail is configured for an app, live sessions are refused. See the API reference for the full /v1/ledger/* surface and SDK reference for the server client.