Hkchain

Redeem stablecoin to fiat

Question. Alice holds 100 hkd.hsbc in her wallet and wants 100 HKD wired to her bank account. What is the end-to-end workflow on Hkchain?

Short answer. MsgBurn (parks the tokens) → issuer wires fiat off-chain → MsgAckRedemption (destroys the parked tokens and records the fiat reference). The tokens are held by the chain in escrow for the duration of the off-chain settlement, which preserves the 1:1 backing invariant at every moment in the lifecycle.

The flow, step by step

1. Alice signs MsgBurn

MsgBurn { holder: alice, amount: 100 hkd.hsbc }

On chain:

  • bankKeeper.SendCoinsFromAccountToModule moves Alice's 100 hkd.hsbc from her EOA into the x/stablecoin module-account pot.
  • Tokens are not destroyed yet — they are parked.
  • A new RedemptionRequest is created:
    { id: N, holder: alice, issuer: hsbc, amount: 100 hkd.hsbc,
      state: OPEN, opened_at: blockTime }
    
  • IssuerTreasury[hsbc][hkd.hsbc].pending_redemption += 100.
  • EventBurned{ holder, amount, request_id } is emitted.
  • MsgBurnResponse returns the request_id so Alice's wallet can follow the request through its lifecycle.

At this moment:

  • Alice's hkd.hsbc balance = 0
  • Module account holds +100 hkd.hsbc
  • Total supply is unchanged
  • HSBC now has an on-chain IOU to pay Alice off-chain

2. HSBC back-office picks up the open request

HSBC's operations system subscribes to EventBurned or periodically queries:

QueryOpenRedemptions { issuer: hsbc }

It treats open redemptions the same way a traditional bank treats withdrawal instructions: look up the customer's fiat bank details from KYC onboarding records (referenced on chain through Identity.ivms101_off_chain_ref), batch, send the wire.

3. HSBC wires fiat to Alice

Off-chain, through whichever rail is appropriate — FPS, CHATS, SWIFT MT103. The outcome is a fiat-side payment reference (FPS transaction id, MT103 reference, etc.).

At this point Alice has already received her money. The on-chain settlement is still open.

4. HSBC signs MsgAckRedemption

MsgAckRedemption { issuer: hsbc, request_id: N, fiat_ref: "FPS-20260421-xxx" }

On chain:

  • bankKeeper.BurnCoins(moduleAccount, 100 hkd.hsbc) — the parked tokens are destroyed. Total supply drops by 100.
  • RedemptionRequest.state flips OPEN → ACKED.
  • acked_at = blockTime, fiat_ref stored on the request row.
  • IssuerTreasury[hsbc][hkd.hsbc].pending_redemption -= 100.
  • EventRedemptionAcked{ issuer, request_id, holder, amount, fiat_ref } is emitted — this is the event regulators and Alice use to reconcile the on-chain burn with the off-chain fiat payment.

5. Alice's wallet view

Tracking the request_id returned in step 1, Alice's wallet / the Dashboard shows:

  • After step 1: "In Progress" (request opened)
  • After step 4: "Settled" with the fiat reference

The block explorer links the EventBurned and EventRedemptionAcked events by request_id.

Why parking, not burn-on-burn?

The tokens represent a claim on HSBC's reserve. Destroying them before HSBC has paid Alice would break 1:1 backing: circulation would drop while the reserve has not yet been debited. On the random-day PoR snapshot the issuer would look over-reserved, which is as much of a reporting defect as being under-reserved.

Parking-then-burning keeps circulation = reserve-backed across the whole lifecycle:

PhaseCirculationModule-account potReserve (off-chain)
Before burn+100 (Alice holds)0100 HKD backing
After MsgBurn0 in Alice's EOA+100 (parked)100 HKD backing (not yet paid out)
After fiat wire0 in Alice's EOA+100 (parked)Reserve −100 (wire sent)
After MsgAckRedemption00 (burned)Reserve −100

Circulation (in issuer-external EOAs) + parked pot = total supply, and total supply = reserve-backed claims at every phase.

Why MsgAckRedemption exists at all

The blockchain cannot settle fiat. It can only witness that fiat was settled. MsgAckRedemption is HSBC's on-chain acknowledgement that the off-chain wire completed, with a fiat reference anchored to the original on-chain request. It is not the payment itself — the payment already happened in step 3.

This is the cleanest separation of concerns: on-chain owns supply accounting and audit trail; off-chain owns actual fiat movement. Regulators reconcile the two through the fiat_ref field and the bank's own transaction records.

Edge cases and what happens

HSBC never acks

The redemption stays OPEN forever. This is a legal / regulatory problem, not a chain problem — the chain has no mechanism to force an issuer to pay fiat. In a real system, HKMA would notice via QueryOpenRedemptions returning requests older than some SLA; the compliance layer (post-MVP) would surface a "stale redemption" flag and escalate to supervisory intervention. MVP does not enforce redemption SLAs on chain.

Alice changes her mind after burning

There is no MsgCancelRedemption in MVP. The tokens are already parked and Alice's balance is already zero. If she changes her mind, the out-of-band path is: ask HSBC to MsgDistribute the same amount back to her. The original redemption request still closes normally with a fiat_ref pointing to "cancelled — redistributed on chain," though the exact convention here is unspecified in MVP.

A MsgCancelRedemption is a plausible post-MVP addition but adds a branching path to the lifecycle and muddies the single-narrative audit story, so the current design keeps the flow linear.

Alice's on-chain view between steps 1 and 4

Her hkd.hsbc balance is 0. She does not have an on-chain "IOU" entry on her own account — the claim lives in the RedemptionRequest row indexed by request_id. Wallets subscribe to EventBurned and store the id for her. Queries by request_id or by holder address return her open requests.

Partial fill

Not supported in MVP. MsgAckRedemption closes the full amount of the original request or the request stays open. A request cannot be half-acked. An issuer who wants to pay in tranches must coordinate that off-chain with the holder; on-chain they see one MsgBurn / one MsgAckRedemption pair.

Multiple issuers (cross-issuer redemption)

Not in MVP — Alice redeems hkd.hsbc with HSBC only. Redeeming hkd.hsbc at Anchorpoint would require a cross-issuer settlement protocol that is out of scope. Inter-issuer swap is its own design surface.

On-chain state summary

State objectTouched by MsgBurnTouched by MsgAckRedemption
Alice's bank balance−100 hkd.hsbc—
Module-account balance+100 hkd.hsbc−100 hkd.hsbc (BurnCoins)
Total supplyunchanged−100 hkd.hsbc
RedemptionRequest[id=N]created with state=OPENflipped to state=ACKED, fiat_ref set
IssuerTreasury.pending_redemption+100−100
EventBurnedemitted—
EventRedemptionAcked—emitted