Contents

Scheduled top-ups

A recurring allowance from any timer, made safe by minting the idempotency key from the period.

Source src/operations/topUp.ts#L30topUpsrc/contract.ts#L76idempotencyKey

A monthly allowance, a daily login grant, a weekly stipend — every recurring credit is the same recipe: any timer you already have, plus an idempotency key minted from the period instead of from the moment.

The timer is deliberately not the interesting part. A cron job, a queue with a delay, or the same Scheduler seam the background worker runs on all work, because the safety lives in the key: fire twice in one period and the second submit is a duplicate, not a second grant.

import {
  createEconomy,
  credits,
  encodeAmount,
  spendable,
  systemActor,
  topUp,
} from '@pwngh/economy-lab';

import type { SnippetReport } from './context.ts';

// A scheduled top-up is any timer plus an idempotency key minted from the period. The key is
// what makes the schedule safe: a cron that fires twice, a retried job, a redeployed worker —
// same period, same key, at most one grant.
export async function run(): Promise<SnippetReport> {
  const economy = await createEconomy();
  const monthly = (period: string) =>
    economy.submit(
      topUp({
        idempotencyKey: `allowance_usr_kid_${period}`, // the period IS the key
        actor: systemActor('allowance'),
        userId: 'usr_kid',
        amount: credits(50),
        source: 'allowance',
      }),
    );

  const july = await monthly('2026-07');
  const julyAgain = await monthly('2026-07'); // the timer double-fired
  const august = await monthly('2026-08');
  const balance = await economy.read.balance(spendable('usr_kid'));
  await economy.close();

  return {
    lines: [
      `2026-07 fired:    ${july.status}`,
      `2026-07 re-fired: ${julyAgain.status} — the key absorbed the retry`,
      `2026-08 fired:    ${august.status}`,
      `two months, one retry, balance: ${encodeAmount(balance)}`,
    ],
    consolePath: '/market',
  };
}

The key discipline is the whole trick, and it is the same one every idempotent retry uses: derive the key from the action’s own identity (allowance_usr_kid_2026-07), never from Date.now() or a random id at fire time. A timer that misfires, a job that retries after a crash, and a deploy that replays a backlog all collapse into the one grant the period was owed.

See also