triggair

Blog / economy

Give your game a currency wallet

3 min read

Give your game a currency wallet cover art

The fastest way to ruin a game economy is to trust the client. If your code says "player earned 100 coins" and the client tells the server to add them, someone will open the console and add a million. Every currency exploit you've ever heard of starts there.

Triggair's economy flips the trust model. A wallet is server-authoritative: currency only enters through a validated path (a purchase, a reward, a loot roll), and there is no client-side "grant" call at all. Your game reads balances and spends them, but it can never mint them. That single design choice removes the entire category of "player gave themselves money" cheats.

Read the wallet

tg.economy.wallet() returns every currency the player holds. A game usually has a couple: a soft currency you earn by playing and a hard currency tied to purchases. Which is which is configured in the dashboard, not hardcoded.

ts
const wallet = await tg.economy.wallet();
// [{ currency: 'coins', balance: 1240 }, { currency: 'gems', balance: 12 }]

for (const { currency, balance } of wallet) {
  renderCurrency(currency, balance);
}

Render this straight into your HUD. It's the player's real, server-held balance, so there's no local counter to drift out of sync or get tampered with.

Check one balance

When you just need to gate a purchase or an upgrade, tg.economy.balance(code) fetches a single currency by its code.

ts
const gems = await tg.economy.balance('gems');

if (gems.balance >= itemPrice) {
  enableBuyButton();
} else {
  showBuyGemsPrompt();
}

Use this to decide what the UI offers. The actual spend is still resolved server-side through your configured economy. The client-side check is for a snappy interface, not for authority.

Show the ledger

Players trust an economy they can audit. tg.economy.history() returns the transaction ledger, every credit and debit, which you can render as a "recent activity" list so a coin balance never feels like it changed for no reason.

ts
const ledger = await tg.economy.history();
// { lines: [{ id, txn_id, target, delta, balance_after, created_at }], next_cursor }

renderTransactions(ledger.lines);

Because every change is a recorded entry, "where did my gems go?" has an answer, and you have a paper trail if you ever need to investigate abuse.

How currency actually arrives

Since the client can't grant, currency flows in through the inbox. A daily reward, a match payout, a purchased pack: each escrows as an inbox item that the player claims, and the claim credits the wallet server-side.

ts
const items = await tg.inbox.list();
for (const item of items) {
  await tg.inbox.claim(item.id); // exactly-once; credits the wallet
}
const updated = await tg.economy.wallet(); // reflects the new balance

That's the whole loop: something on the server decides a reward is owed, it lands in the inbox, the player claims it exactly once, and the balance updates. Nowhere does the client assert a number.

Gotchas

  • There is no grant from the client, by design. If you're looking for a way to add currency in code, that's the cheat vector Triggair closed. Rewards come from server paths (daily, matches, loot, purchases).
  • Soft vs hard currency is a dashboard setting. Define your currency codes, which are purchasable, and their display there, then just read them by code in the SDK.
  • Treat client-side balance checks as UI hints, not authorization. The spend is validated where it counts.

Read balances for your HUD, check one for a gate, show history for trust, and let the inbox be the only door currency walks through. That's an economy players can't cheat.

Build this into your game.

Everything above is one import and a publishable key away. Start with the quickstart, or read the guide for the exact feature.

Keep reading