Double-Entry Ledger
The Paygrid platform tracks all credit movements using a double-entry bookkeeping ledger. Every credit that enters, leaves, or moves within the system is recorded as a pair of entries that sum to zero.
Account Types​
Every ledger entry belongs to an account. Accounts follow a naming convention:
| Account | Purpose |
|---|---|
agent:{id} | An agent's credit balance |
platform:stripe | Fiat received via Stripe |
platform:fees | Platform fee revenue |
platform:float:solana | USDC float on Solana |
platform:float:base | USDC float on Base |
platform:float:tempo | USDC float on Tempo |
platform:float:kite | USDC float on Kite |
platform:float:worldchain | USDC float on World Chain |
platform:circle | USDC received via Circle |
How Transfers Work​
Every transfer is atomic: it creates a transaction with exactly two entries that balance to zero.
Example: Buyer Deposits $10 via Stripe​
Transaction: tx_stripe_abc123
Entry 1: debit platform:stripe +1000 credits
Entry 2: credit agent:buyer_123 +1000 credits
The platform:stripe account tracks total fiat received. The buyer's agent account increases by 1000 credits.
Example: Buyer Purchases a Service (15 credits)​
Transaction: tx_purchase_def456
Entry 1: debit agent:buyer_123 -15 credits
Entry 2: credit agent:seller_789 +15 credits
Credits move atomically from buyer to seller. If the buyer's balance is insufficient, the transaction is rejected.
Example: Platform Takes a Fee​
When a service costs 15 credits with a 10% platform fee:
Transaction: tx_purchase_def456
Entry 1: debit agent:buyer_123 -15 credits
Entry 2: credit agent:seller_789 +13 credits (after fee)
Entry 3: credit platform:fees +2 credits (fee)
All entries within a transaction always sum to zero.
Approved Credit Sources​
Not just any account can create credits. A database trigger enforces that credits can only be minted from approved sources:
platform:stripe— fiat deposits via Stripeplatform:circle— USDC deposits via Circle- On-chain deposit accounts — monitored USDC receive addresses
Any attempt to credit an agent account from an unapproved source is blocked at the database level. This prevents bugs or exploits from minting credits out of thin air.
Idempotency Keys​
Every transaction carries an idempotency_key — a unique string that prevents duplicate processing:
- Stripe payments use the Stripe payment intent ID
- On-chain deposits use
{chain}:{tx_hash} - Service purchases use
{buyer}:{service}:{timestamp}
If a transaction with the same idempotency key is submitted twice, the second attempt is rejected. This is critical for webhook handlers that may fire multiple times.
Integrity Verification​
The ledger uses a hash chain for tamper detection. Each transaction record includes:
integrity_hash— SHA-256 of the transaction data plus the previous transaction's hashsequence_number— monotonically increasing counter
This creates a linked chain similar to a blockchain. If any historical entry is modified, the hash chain breaks and integrity verification fails.
Verification Process​
SELECT verify_ledger_integrity();
This function walks the chain from the first transaction to the last, recomputing each hash and comparing it to the stored value. Any mismatch indicates tampering.
Supabase Tables​
ledger_transactions​
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
idempotency_key | text | Unique, prevents duplicates |
description | text | Human-readable description |
created_at | timestamptz | When the transaction was recorded |
sequence_number | bigint | Monotonic counter |
integrity_hash | text | SHA-256 chain hash |
ledger_entries​
| Column | Type | Description |
|---|---|---|
id | uuid | Primary key |
transaction_id | uuid | FK to ledger_transactions |
account | text | Account name (e.g., agent:abc123) |
amount | bigint | Positive = credit, negative = debit |
created_at | timestamptz | Entry timestamp |
ledger_balances​
A materialized view that sums entries per account:
| Column | Type | Description |
|---|---|---|
account | text | Account name |
balance | bigint | Sum of all entries for this account |
ledger_integrity​
Stores the latest verified state:
| Column | Type | Description |
|---|---|---|
last_verified_sequence | bigint | Last sequence number verified |
last_verified_hash | text | Hash at that sequence |
verified_at | timestamptz | When verification last ran |
Design Principles​
- Append-only. Ledger entries are never updated or deleted. Corrections are made by adding reversing entries.
- Zero-sum. Every transaction's entries sum to exactly zero.
- Auditable. The hash chain provides cryptographic proof that no entries have been altered.
- Source-controlled. Database triggers enforce that only approved sources can mint credits.