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.
| # | Gate | Blocks or flags | Rejects when |
|---|---|---|---|
| 1 | kyc_gate | Blocks | Sender or recipient is blacklisted |
| 2 | sanctions | Blocks | Either party's VASP is on the sanctions list |
| 3 | travel_rule | Blocks | Payload missing, tier mismatched, or fields inconsistent with the identity registry |
| 4 | threshold | Flags | Amount is at or above the enhanced-data threshold |
| 5 | velocity | Flags | Transfer density inside the rolling window is unusual |
| 6 | role_gate | Blocks | A delegated key signed a recognised message its sub-role is not allowed to sign |
| 7 | agent_policy | Blocks | An 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.MsgSendcarrying anhkd.*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.
MsgSendis 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 path | EVM path | |
|---|---|---|
| Gates run | Twice — at admission and at execution | Once, during execution |
| A gate rejection is | A rejected transaction, usually visible in the broadcast response | A revert inside a transaction that is in a block |
| You read the verdict from | Broadcast response, then the transaction query | The receipt / transaction query |
| Gas | Not consumed on an admission rejection | Consumed 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_callagainst 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.