Hkchain

Transaction lifecycle

One transfer, from your process to a committed block. This page is the map; the three that follow are the mechanics.

There are two lifecycles, not one. A Cosmos transaction meets the compliance gates before execution; an EVM call meets them during execution. Everything below the diagram describes the Cosmos path, and the section "The EVM path reaches the same gates at a different point" states where the other one differs. The difference decides where you read the verdict.

The Cosmos path

your process            node                                 network
────────────────────────────────────────────────────────────────────────
build   ──────────▶
sign    ──────────▶  broadcast ─▶ CheckTx ─▶ mempool ─▶ block ─▶ execution
                                    │                             │
                                    ▼                             ▼
                              generic checks               generic checks
                              signature                    signature
                              fee                          fee
                              COMPLIANCE GATES             COMPLIANCE GATES
                                    │                             │
                                    ▼                             ▼
                              reject → response           execute message
                                                          emit typed events
                                                          commit state

Three things to take from the diagram.

The compliance gates run twice, and they run before execution. They sit in the pre-execution phase that every Cosmos transaction passes through, so a rejected transfer never reaches the module that would have moved the money. There is no partial application to unwind.

A rejection at the first pass costs you nothing but the round trip. The gates are positioned after the signature is verified — so the chain knows who you are — but before the account sequence advances. A transaction rejected there leaves the account exactly as it was; you can fix the problem and re-sign at the same sequence.

Passing the first pass is not a promise about the second. The two passes run against different state. A recipient blacklisted between them makes the same transaction pass at admission and fail in the block. The account is still untouched — the gates sit before the sequence advances on both passes, so nothing is deducted and the sequence is not consumed — but the verdict moves: the transaction is now in a block with a non-zero code, and you read it from the transaction query rather than from the broadcast response. Broadcasting and confirmation covers telling the two apart.

What runs at the gate

Seven gates, always in this order. Five apply to stablecoin movement, two to delegated signing.

#GateBlocks or flagsRejects when
1kyc_gateBlocksSender or recipient is blacklisted
2sanctionsBlocksEither party's VASP is on the sanctions list
3travel_ruleBlocksPayload missing, tier mismatched, or fields inconsistent with the identity registry
4thresholdFlagsAmount is at or above the enhanced-data threshold
5velocityFlagsTransfer density inside the rolling window is unusual
6role_gateBlocksA delegated key signed a recognised message its sub-role is not allowed to sign
7agent_policyBlocksAn agent key exceeded its per-transaction or 24-hour cap, or its delegation was revoked

A flagging gate never rejects. It opens a compliance flag for a human reviewer and lets the transaction proceed. Do not build retry logic around gates 4 and 5 — from the transaction's point of view they always pass.

Delegated keys: identity resolves, attribution does not

Before a gate reads an identity, the signer is resolved to its parent if it is a delegated key. Gates 1, 2, 3 and the VASP tag on gate 4 therefore evaluate the level, VASP and identity references of the account that delegated, not of the key that signed.

Attribution is the other half, and it does not follow the same rule. The flags and audit entries written by gates 4 and 5 record the signing key as their subject, and the velocity count in gate 5 is kept per signing key rather than per identity — two agents under one parent do not pool into one burst. Gate 7's caps are likewise held per delegation pair, not per parent.

So: who is allowed to do this is answered by the parent. Who did this is recorded as the key. An integration that displays activity has to resolve the parent itself; see Accounts and keys.

What the gates recognise, and what they do not

A gate only engages if the transaction contains something it recognises as value movement in a stablecoin. The recognised set is exactly three message shapes:

  • cosmos.bank.v1beta1.MsgSend carrying an hkd.* coin (the first such coin, if several),
  • MsgDistribute — an issuer distributing stablecoin to a holder,
  • MsgBurn — a holder burning stablecoin for redemption.

Anything else is not evaluated. For most of what a transaction can contain that is the intended design and the guarding lives elsewhere: administrative, staking and governance messages are gated by the authority checks inside the handler that executes them, and a native ahkc transfer is deliberately outside the perimeter — it skips the entire chain above, including from a blacklisted address. The compliance perimeter is the stablecoin, not the chain.

But "not evaluated" is literal, and it is worth stating plainly rather than inferring a guarantee that is not there. A message that moves an hkd.* balance in some other shape — a bank multi-send, an authorisation-grant execution wrapping a send, an IBC transfer — is not in the recognised set, so it produces no compliance fact and no gate inspects it. The same applies to the delegated allowlist in gate 6: it is enforced over the message types the gate knows how to extract a signer from, and an unrecognised message falls through rather than being rejected.

Two consequences for an integration:

  • Build stablecoin movement on the recognised shapes. MsgSend is the supported transfer, and it is the one every compliance surface — flags, audit log, traces, reports — will show your transfer under.
  • Do not treat "the chain would have stopped it" as a substitute for your own controls on message construction. The gate chain is the enforcement point for the shapes it recognises, which is a narrower claim than "every path to an hkd.* balance".

The EVM path reaches the same gates at a different point

A transfer that arrives as an EVM call meets the same seven gates, but not in the same phase, and this changes where you read the outcome.

An EVM transaction is routed by its first extension option to the EVM pre-execution path, which does not contain the compliance gates. The gates run later, inside the extended ERC-20 interface while the call executes: it decodes the Travel Rule payload from its argument, synthesises the equivalent bank send, and runs the same gate chain before delegating to the transfer.

Cosmos pathEVM path
Gates runTwice — at admission and at executionOnce, during execution
A gate rejection isA rejected transaction, usually visible in the broadcast responseA revert inside a transaction that is in a block
You read the verdict fromBroadcast response, then the transaction queryThe receipt / transaction query
GasNot consumed on an admission rejectionConsumed up to the revert

The rejection reason itself is identical — the gate's message is carried up verbatim as the revert reason.

This is also why the plain ERC-20 transfer reverts unconditionally: it has nowhere to carry a payload, so honouring it would create a path to the same balances that no gate had inspected.

Finality

Consensus is instant-finality: a transaction included in a committed block is final at that block. There is no confirmation-depth heuristic, no reorg to wait out. When the transaction query returns your transaction with a height, it is done.

What that does not mean is that a successful broadcast is a successful transaction — see Broadcasting and confirmation for the two distinct outcomes hiding behind one response.

Where the trace comes from

Each pass through the gate chain is recorded: gate name, blocking or advisory, PASS / FAIL / FLAGGED, a readable reason, and gas consumed. Rejected transactions are recorded too — the partial trace up to the failure is kept, so a rejection is diagnosable rather than opaque. Transfers that entered through the EVM interface are recorded as well, keyed by the Cosmos transaction hash of the wrapping transaction rather than by the Ethereum transaction hash your Ethereum tooling reports.

A trace is not chain state. It lives in a bounded in-memory ring on the node that ran it — 256 entries, retained for one hour, whichever bound is hit first — and contributes nothing to the application hash. Three consequences for an integration:

  • A trace older than the retention window, or pushed out by newer traffic, is gone. For a Cosmos transaction the chain offers a simulation over the original transaction bytes that reproduces the same trace; that simulation decodes a Cosmos transaction and is not an EVM call simulator, so the EVM equivalent is a read-only eth_call against the same method.
  • Traces are per node. Behind a load balancer, the node you ask may not be the node that ran the transaction.
  • A trace is a diagnostic, not a record. The durable compliance record is the audit log and the flags, which are state.

Errors and rejections covers reading all of these.

Where to go next