Skip to content
_STABLECOIN ISSUERS/

Issue stablecoins on a ledger that proves its reserves

Back every token 1:1 with segregated fiat, mint and redeem programmatically, and produce an immutable audit trail your auditors and regulators can trust.

reserve · ledger
LIVE
USD/2 1,000,000
world
reserve:fiat
USD/2 1,000,000
treasury:minted
holders:alice
USD/2 250,000
holders:bob
treasury:burned
USD/2 250,000
reserve:fiat
world
EUR/2 480,000
world
reserve:fiat
EUR/2 480,000
treasury:minted
holders:carol
_THE PROBLEM/

Spreadsheets cannot back a token

Reserve integrity is an accounting problem, not a smart-contract one.

Off-chain reserves, mint/burn events and bank balances live in disconnected systems. Reconciling them is manual, error-prone, and impossible to prove after the fact.

Policydaily-stripe-recon
NOT_OK
Drift detected0.50
asset USD/2run 01HGW4D7SXRQ
Balance compareUSD/2
Ledger balance1,000.00
Cash pool999.50
Drift0.50
error:balance drift for asset USD/2

ledger @ 23:59:59 ≠ cash pool @ 23:59:59 · flagged for review

0Reserve balances drift from circulating supply with no single source of truth.
1Mint and redeem flows are scripted ad-hoc, with no idempotency or replay safety.
2Auditors ask for point-in-time reserve proofs your stack cannot reconstruct.
3Every new banking partner or rail means another bespoke integration.

_PROOF/ Stables runs its reserve ledger on Formance — every token traceable to a segregated dollar.

_FLOW OF FUNDS/

Every token traces back to a dollar

Fiat in, token out — one auditable chain of double-entry transactions.

Deposits land in a segregated reserve, minting is posted against that reserve, and redemptions reverse the exact path. Balances are derived, never overwritten.

proof of reserves
fiat depositbank → reserve
reservesegregated 1:1
minttoken issued
redeemburn → payout
_CAPABILITIES/

The primitives issuers need

Built on the open-source Formance ledger. A double-entry core, programmable money movement and normalized connectivity — composed for reserve-backed issuance.

_RESERVE LEDGER/

point-in-time balances

RESERVE LEDGER

A double-entry account tree segregating fiat reserves per currency and per banking partner, with derived balances you can query at any timestamp.

GET200/v2/reserve/aggregate/balances?pit=2026-07-04T23:59:59Z
Aggregated reserves · as of PIT4,820,000.20EUR/2
EUR/2 · bnp4,820,000.20
USD/2 · column3,145,050.13
GBP/2 · clear912,426.04
PIT
2026-07now
_MINT / REDEEM/

exactly-once

MINT / REDEEM

Model issuance and redemption as deterministic Numscript transactions — idempotent, exactly-once, and reversible by construction.

Read the Numscript docs
_MINT/issuance
send [POINTS 500] (
  source = @world
  destination = @users:alice
)
_REDEEM/redemption
send [POINTS 500] (
  source = @users:alice
  destination = @world
)
_EXACTLY-ONCE/Idempotency-Key: mint-8841
TRY 1write
TRY 2hit: true
TRY 3hit: true
TX #4021POINTS +500
×1one write

deterministic · idempotent · reversible by construction

_PROOF OF RESERVES/

immutable log

PROOF OF RESERVES

Reconstruct circulating supply and backing reserves for any point in time, straight from the immutable transaction log.

proof of reserves · audit
LIVE
MATCH
reserve:fiat ↔ supply:USDC USD 1,000,000
MATCH
reserve:fiat ↔ supply:EURC EUR 480,000
DRIFT
D-1 supply vs reserve USD 0.00
MATCH
holders:alice ↔ mint tx USD 1,000,000
RESOLVE
point-in-time snapshot · T-0
MATCH
holders:bob ↔ redeem tx USD 250,000
MATCH
reserve:fiat ↔ supply:USDC USD 1,000,000
_BANKING CONNECTIVITY/

one schema, every rail

BANKING CONNECTIVITY

Normalize deposits and payouts from every bank and PSP into one schema, so adding a reserve partner never changes your logic.

1schema21 connectors
payments · normalized
LIVE
Stripe logo
PAY-INSEPA · EUR/2
+8,210.00
SUCCEEDED
Column logo
PAYOUTACH · USD/2
−12,400.00
PENDING
Stripe logo
PAY-INCARD_VISA · USD/2
+129.99
SUCCEEDED
Fireblocks logo
TRANSFEROTHER · BTC/8
−0.5200
SUCCEEDED
Wise logo
PAYOUTSEPA · EUR/2
−3,000.00
FAILED
_BY THE NUMBERS/

Reserve-grade, by design

reserve backing1:1segregated · per-currency
auditable history100%immutable · point-in-time
mint & redeemexactly-onceidempotent · replay-safe
time to integratedaysopen-source core
_LEDGER SCHEMA/

Every mint, redeem and rebalance — enforced by the chart

The YAML is the source of truth: chart of accounts, typed transactions, and queries. Watch the schema light up as each posting lands.

schema.yaml
chart:
  platform:
    banks:
      $bankId:
        reserve: {}
        yield:
          accrued: {}
    mints:
      $mintId:
        inTransit: {}
    redemptions:
      $redemptionId:
        settling: {}
        payable: {}
    reserves:
      rebalance:
        $rebalanceId:
          inTransit: {}
    fees:
      redemption: {}
    revenue:
      yield: {}
  holders:
    $holderId: {}
    .self: {}
  counterparties:
    banks:
      $bankId: {}
  external:
    fiat:
      wires: {}
      payouts: {}
    networks:
      $networkId:
        supply: {}
transactions:
  MINT_INITIATE:
    description: >-
      Holder fiat wire acknowledged for a mint; cash recorded in transit, no
      token credited yet
    interpreter: experimental
    script: |-
      vars {
        account $mint_id
        monetary $fiat_amount
        string $mint_ref
      }

      send $fiat_amount (
        source = @external:fiat:wires allowing unbounded overdraft
        destination = @platform:mints:$mint_id:inTransit
      )

      set_tx_meta("event_type", "mint_initiate")
      set_tx_meta("mint_id", $mint_id)
      set_tx_meta("mint_ref", $mint_ref)
  MINT_SETTLE:
    description: >-
      Reserve bank confirms settlement; in-transit fiat becomes reserve and
      token is minted to the holder
    interpreter: experimental
    script: |-
      vars {
        account $bank_id
        account $mint_id
        account $holder_id
        account $network_id
        monetary $fiat_amount
        monetary $token_amount
        string $mint_ref
      }

      send $fiat_amount (
        source = @platform:mints:$mint_id:inTransit
        destination = @platform:banks:$bank_id:reserve
      )

      send $token_amount (
        source = @external:networks:$network_id:supply allowing unbounded overdraft
        destination = @holders:$holder_id
      )

      set_tx_meta("event_type", "mint_settle")
      set_tx_meta("mint_id", $mint_id)
      set_tx_meta("mint_ref", $mint_ref)
  MINT_RETURN:
    description: >-
      Mint wire reversed before settlement; in-transit fiat returned, no token
      minted
    interpreter: experimental
    script: |-
      vars {
        account $mint_id
        monetary $fiat_amount
        string $original_posting_id
      }

      send $fiat_amount (
        source = @platform:mints:$mint_id:inTransit
        destination = @external:fiat:wires
      )

      set_tx_meta("event_type", "mint_return")
      set_tx_meta("mint_id", $mint_id)
      set_tx_meta("adjustment_flag", "true")
      set_tx_meta("adjusted_posting_event_id", $original_posting_id)
  TRANSFER:
    description: >-
      Instant peer-to-peer token transfer between two holders, no reserve
      movement
    interpreter: experimental
    script: |-
      vars {
        account $from_holder_id
        account $to_holder_id
        monetary $token_amount
        string $transfer_ref
      }

      send $token_amount (
        source = @holders:$from_holder_id
        destination = @holders:$to_holder_id
      )

      set_tx_meta("event_type", "transfer")
      set_tx_meta("transfer_ref", $transfer_ref)
  TRANSFER_REVERSE:
    description: Reversal of a prior token transfer, tokens returned to the original sender
    interpreter: experimental
    script: |-
      vars {
        account $from_holder_id
        account $to_holder_id
        monetary $token_amount
        string $original_posting_id
      }

      send $token_amount (
        source = @holders:$to_holder_id
        destination = @holders:$from_holder_id
      )

      set_tx_meta("event_type", "transfer_reverse")
      set_tx_meta("adjustment_flag", "true")
      set_tx_meta("adjusted_posting_event_id", $original_posting_id)
  REDEEM_REQUEST:
    description: >-
      Holder burns token for redemption; fiat payout obligation and 10 bps fee
      booked, reserve untouched until settlement
    interpreter: experimental
    script: |-
      vars {
        account $holder_id
        account $network_id
        account $redemption_id
        monetary $token_amount
        monetary $gross_fiat
        monetary $fee
        string $redemption_ref
      }

      send $token_amount (
        source = @holders:$holder_id
        destination = @external:networks:$network_id:supply
      )

      send $gross_fiat (
        source = @platform:redemptions:$redemption_id:settling allowing unbounded overdraft
        destination = {
          max $fee to @platform:fees:redemption
          remaining to @platform:redemptions:$redemption_id:payable
        }
      )

      set_tx_meta("event_type", "redeem_request")
      set_tx_meta("redemption_id", $redemption_id)
      set_tx_meta("redemption_ref", $redemption_ref)
  REDEEM_SETTLE:
    description: >-
      Reserve bank settles the redemption payout; reserve drains, net fiat
      leaves to the holder
    interpreter: experimental
    script: |-
      vars {
        account $bank_id
        account $redemption_id
        monetary $gross_fiat
        monetary $net_fiat
        string $redemption_ref
      }

      send $gross_fiat (
        source = @platform:banks:$bank_id:reserve
        destination = @platform:redemptions:$redemption_id:settling
      )

      send $net_fiat (
        source = @platform:redemptions:$redemption_id:payable
        destination = @external:fiat:payouts
      )

      set_tx_meta("event_type", "redeem_settle")
      set_tx_meta("redemption_id", $redemption_id)
      set_tx_meta("redemption_ref", $redemption_ref)
  REDEEM_RETURN:
    description: >-
      Settled redemption payout returned; redemption cancelled, reserve and
      token restored to the holder
    interpreter: experimental
    script: |-
      vars {
        account $bank_id
        account $holder_id
        account $network_id
        account $redemption_id
        monetary $token_amount
        monetary $net_fiat
        monetary $fee
        string $original_posting_id
      }

      send $net_fiat (
        source = @external:fiat:payouts allowing unbounded overdraft
        destination = @platform:banks:$bank_id:reserve
      )

      send $fee (
        source = @platform:fees:redemption allowing unbounded overdraft
        destination = @platform:banks:$bank_id:reserve
      )

      send $token_amount (
        source = @external:networks:$network_id:supply allowing unbounded overdraft
        destination = @holders:$holder_id
      )

      set_tx_meta("event_type", "redeem_return")
      set_tx_meta("redemption_id", $redemption_id)
      set_tx_meta("adjustment_flag", "true")
      set_tx_meta("adjusted_posting_event_id", $original_posting_id)
  REBALANCE_INITIATE:
    description: >-
      Reserve cash sent from one reserve bank toward the other; held in transit
      until it settles
    interpreter: experimental
    script: |-
      vars {
        account $from_bank_id
        account $rebalance_id
        monetary $fiat_amount
        string $rebalance_ref
      }

      send $fiat_amount (
        source = @platform:banks:$from_bank_id:reserve
        destination = @platform:reserves:rebalance:$rebalance_id:inTransit
      )

      set_tx_meta("event_type", "rebalance_initiate")
      set_tx_meta("rebalance_id", $rebalance_id)
      set_tx_meta("rebalance_ref", $rebalance_ref)
  REBALANCE_SETTLE:
    description: Interbank reserve transfer settles into the destination bank's reserve
    interpreter: experimental
    script: |-
      vars {
        account $to_bank_id
        account $rebalance_id
        monetary $fiat_amount
        string $rebalance_ref
      }

      send $fiat_amount (
        source = @platform:reserves:rebalance:$rebalance_id:inTransit
        destination = @platform:banks:$to_bank_id:reserve
      )

      set_tx_meta("event_type", "rebalance_settle")
      set_tx_meta("rebalance_id", $rebalance_id)
      set_tx_meta("rebalance_ref", $rebalance_ref)
  REBALANCE_RETURN:
    description: Failed interbank reserve transfer returned to the origin bank's reserve
    interpreter: experimental
    script: |-
      vars {
        account $from_bank_id
        account $rebalance_id
        monetary $fiat_amount
        string $original_posting_id
      }

      send $fiat_amount (
        source = @platform:reserves:rebalance:$rebalance_id:inTransit
        destination = @platform:banks:$from_bank_id:reserve
      )

      set_tx_meta("event_type", "rebalance_return")
      set_tx_meta("rebalance_id", $rebalance_id)
      set_tx_meta("adjustment_flag", "true")
      set_tx_meta("adjusted_posting_event_id", $original_posting_id)
  YIELD_ACCRUE:
    description: >-
      Reserve bank credits interest on reserves into a segregated yield-accrued
      sub-account
    interpreter: experimental
    script: |-
      vars {
        account $bank_id
        monetary $yield_amount
        string $accrual_period
      }

      send $yield_amount (
        source = @counterparties:banks:$bank_id allowing unbounded overdraft
        destination = @platform:banks:$bank_id:yield:accrued
      )

      set_tx_meta("event_type", "yield_accrue")
      set_tx_meta("accrual_period", $accrual_period)
  YIELD_SWEEP:
    description: Monthly sweep of accrued reserve yield into operational revenue
    interpreter: experimental
    script: |-
      vars {
        account $bank_id
        monetary $yield_amount
        string $sweep_period
      }

      send $yield_amount (
        source = @platform:banks:$bank_id:yield:accrued
        destination = @platform:revenue:yield
      )

      set_tx_meta("event_type", "yield_sweep")
      set_tx_meta("sweep_period", $sweep_period)
queries:
  parity_invariant_supply_vs_total_backing:
    description: >-
      The load-bearing 1:1 proof, run daily. Total circulating token supply must
      equal total settled fiat reserve plus reserve backing in motion (interbank
      rebalances), with redemptions that are burned-but-not-yet-paid subtracted
      because their backing is owed out. Mint cash in transit is excluded: those
      tokens are not yet minted, so that fiat is not yet backing. The two sides
      are read as separate balance queries and compared by the caller; the
      equality is stated below in prose.
    resource: accounts
    body:
      $match:
        address: 'holders:'
  cross_check_holder_supply_vs_network_supply:
    description: >-
      A second, independent measure of total circulating supply: the sum of the
      per-network on-chain boundaries. The boundaries run negative (tokens
      issued into circulation), so their absolute value is circulating supply.
      This must equal the holder-side total; a drift means a mint or burn
      touched one side without the other.
    resource: accounts
    body:
      $match:
        address: 'external:networks::supply'
  per_bank_reserve_balance:
    description: >-
      Run per bank against that bank's statement. The reserve account holds
      settled backing; reconciliation passes when the ledger balance equals the
      bank's reported segregated-reserve balance for that account. Substitute
      the concrete bank id.
    resource: accounts
    body:
      $match:
        address: platform:banks:bankA:reserve
  total_circulating_supply_holder_side:
    description: >-
      Sums every holder's token balance into the platform-wide circulating
      liability. Run on demand or on the daily reporting schedule.
    resource: accounts
    body:
      $match:
        address: 'holders:'
  per_holder_circulating_balance:
    description: One holder's circulating token balance. Substitute the holder id.
    resource: accounts
    body:
      $match:
        address: holders:H001
  per_network_circulating_supply:
    description: >-
      Circulating supply on one network, read from that network's on-chain
      boundary (runs negative; take the absolute value). Substitute the network
      id. The whole-fleet rollup uses external:networks::supply.
    resource: accounts
    vars:
      networkId:
        type: string
    body:
      $match:
        address: external:networks:${networkId}:supply
  total_settled_reserve:
    description: >-
      Sums the settled reserve across both reserve banks. This is the
      settled-backing figure in the parity proof.
    resource: accounts
    body:
      $match:
        address: platform:banks::reserve
  in_flight_backing_dashboard:
    description: >-
      Every fiat amount that is in motion or owed: mint cash in transit
      (pre-backing), redemptions settling (backing owed out), and interbank
      rebalances in transit (backing in motion). Run as an operational dashboard
      to see all in-flight fiat at a glance.
    resource: accounts
    body:
      $or:
        - $match:
            address: platform:mints::inTransit
        - $match:
            address: platform:redemptions::settling
        - $match:
            address: platform:reserves:rebalance::inTransit
  accrued_yield_awaiting_sweep:
    description: >-
      Interest credited by the reserve banks but not yet swept to revenue, held
      in the segregated yield-accrued sub-accounts. Reconciles against each
      bank's reported interest.
    resource: accounts
    body:
      $match:
        address: platform:banks::yield:accrued
  daily_redemption_fee_revenue:
    description: >-
      The accrued redemption-fee balance. For the daily figure, read flow into
      this account over the day with the volumes endpoint below; for the running
      total, read the balance here.
    resource: accounts
    body:
      $match:
        address: platform:fees:redemption
  swept_yield_revenue:
    description: The running total of reserve yield booked as operational revenue.
    resource: accounts
    body:
      $match:
        address: platform:revenue:yield
  daily_redemption_fee_revenue_flow:
    description: >-
      Fee revenue earned in a day, read as volume into the redemption-fee
      account over the daily window.
    resource: volumes
    body:
      $match:
        address: platform:fees:redemption
  reserve_settlement_throughput:
    description: >-
      Fiat settled into and out of one reserve bank over a period (mint
      settlements in, redemption settlements out). Substitute the bank id and
      window.
    resource: volumes
    body:
      $match:
        address: platform:banks:bankA:reserve
  per_holder_token_throughput:
    description: >-
      Token in and out of one holder over a period (mints, transfers,
      redemptions). Substitute the holder id and window.
    resource: volumes
    body:
      $match:
        address: holders:H001
  aging_mints_in_transit:
    description: >-
      Per-mint in-transit accounts that should drain to zero at settlement or
      return. A non-zero balance is a mint that is wired but neither settled nor
      returned; cross-reference the transaction timestamp against the expected
      settlement window.
    resource: accounts
    body:
      $and:
        - $match:
            address: platform:mints::inTransit
        - $not:
            $match:
              balance: 0
  aging_redemptions_settling:
    description: >-
      Per-redemption settling accounts with an outstanding balance: token burned
      but fiat payout not yet settled. Non-zero is the in-transit redemption
      population.
    resource: accounts
    body:
      $and:
        - $match:
            address: platform:redemptions::settling
        - $not:
            $match:
              balance: 0
  aging_rebalances_in_transit:
    description: >-
      Per-rebalance in-transit accounts that have not settled or returned.
      Non-zero past the expected interbank window is a stuck rebalance.
    resource: accounts
    body:
      $and:
        - $match:
            address: platform:reserves:rebalance::inTransit
        - $not:
            $match:
              balance: 0
  all_transactions_touching_one_holder:
    description: >-
      Every posting that hit a holder, in either direction, for drill-down.
      Substitute the holder id.
    resource: transactions
    body:
      $match:
        account: holders:H001
  trace_one_redemption_end_to_end:
    description: >-
      Every transaction tagged with a redemption id, across request, settlement,
      and any return. Substitute the redemption id.
    resource: transactions
    body:
      $match:
        metadata[redemption_id]: R001
stablecoin-issuance · chart enforcedschema v1
POST/api/ledger/v2/stablecoin-issuance/transactions
MINT_INITIATEproposed

Holder fiat wire acknowledged for a mint; cash recorded in transit, no token credited yet

external:fiat:wiresplatform:mints:mint_id:inTransit
1 proposed01 / 13
AI proposes·you approve·the ledger enforces
Try it out on a real ledger with the Studio
_VOICES/

Trusted by builders

LiberisLiberis
Formance enabled us to ship new lending products faster by providing a customizable foundation that let us reliably express the complexities of our flow of funds.

Embedded finance · reconciliation across 14 countries

See customer story
DoctolibDoctolib
Formance is the foundation of our Financial OS, the open-source approach lets us retain control over this key component.

Healthcare · Financial OS

PayflipPayflip
Formance helped us kick off our move into fintech, with robust infrastructure and intuitive developer tooling.

Benefits · fintech infrastructure

_TRUST/

Built for regulated money.

Enterprise controls, the certifications auditors ask for, and an immutable record, out of the box.

See our trust center
THE COMPLIANCE STACK
IAM, RBAC & SSO
Non-repudiation
Audit logs
Observability & monitoring
Real-time events
Admin console
_AUDIT-LOG/ NON-REPUDIATION

09:41:07Z AUDIT gateway POST /api/ledger/v2/main/transactions 200 sub:ops@acme.io

09:41:09Z LOG id:4093 NEW_TRANSACTION ledger:main

09:41:12Z LOG id:4094 SET_METADATA ledger:main

09:41:15Z LOG id:4095 REVERTED_TRANSACTION ledger:main

09:41:18Z AUDIT gateway GET /api/ledger/v2/main/logs 200 sub:audit@acme.io

CERTIFICATIONS
AICPA SOC IICERTIFIED
ISO 27001CERTIFIED
DORACOMPLIANT
REGULATORY FRAMEWORKS
EMIFCAMTLNYDFS Trust CharterOCC CharterMiCAGENIUS
_ATTESTED/
FAQ

Stablecoin issuance, answered

01 / HOW IS 1:1 BACKING ENFORCED?

Every mint is posted against a matching fiat deposit in a segregated reserve account. Circulating supply and reserves are derived from the same immutable log, so backing is verifiable at any timestamp — not asserted after the fact.

02 / CAN I PRODUCE PROOF OF RESERVES?

Yes. Reconstruct circulating supply and backing reserves for any point in time straight from the transaction log, and hand auditors a reproducible figure instead of a spreadsheet snapshot.

03 / ARE MINT/REDEEM FLOWS SAFE TO RETRY?

Issuance and redemption are deterministic Numscript transactions — idempotent and exactly-once. A retried or replayed request can never double-mint or double-burn.

04 / WHAT ABOUT MULTIPLE BANKING PARTNERS?

Deposits and payouts from every bank and PSP normalize into one schema. Adding a reserve partner is a connector change, not a rewrite of your issuance logic.

_GET STARTED/

Build on infrastructure you can trust

See how Formance fits your stack, book a demo, or start building today.