Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions docs/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,36 @@ genesis: `0x…0100`, `0x…0400`, `0x…0800`, `0x…0801`, `0x…0802`, `0x…

### `ICompliance` at `0x…0900`

What a freeze does today, precisely: it stops the address from **signing**
transactions and from being the direct `to` of one — EVM and Cosmos alike,
checked before execution (see
[Troubleshooting](/troubleshooting#address-is-frozen)). It does **not**
immobilise the address's balance: KASH can still be pulled out through the
werc20 precompile or any contract by an allowance granted before the
freeze, and can still arrive by contract call, because the check runs on
signers and the transaction's `to`, not on bank transfers. A contract that
must not serve a frozen address therefore has to call `isFrozen` itself.
(A bank-level restriction is planned before mainnet; the chain's own
tracker records it as open.) That is what this precompile is for. All
What a freeze does, precisely: **nothing can leave a frozen address, and no
one's transaction can fund it.** It binds at every level, EVM and Cosmos alike
(see [Troubleshooting](/troubleshooting#address-is-frozen)):

- The address cannot **sign**, and cannot be a transaction's direct `to` or
recipient. The transaction is refused at submission and nothing is charged.
- An EVM call to `transfer` / `transferFrom` on WKASH or any other
`x/erc20` token precompile that **names** a frozen address is refused at
submission too. An allowance granted before the freeze cannot be spent.
- **Every bank transfer** checks the list: precompiles, IBC transfers,
authz, fee grants. Called from a contract, such a transfer reverts with a
reason (`execution reverted: sender kons1…: address is frozen`), visible in
`eth_call`, `eth_estimateGas` and the receipt.
- A contract's **internal value transfer** (`CALL{value}`) to or from a
frozen address fails when the transaction commits. The transaction was
accepted and gas is charged, but it does not appear in `eth_*`; the reason
is only in CometBFT's `tx_search`. A 0-value call to a frozen address still
succeeds.
- A frozen **contract** cannot stake, vote or redirect rewards through the
staking, distribution or gov precompiles.

One direction stays open: **protocol completions may still credit a frozen
address**. These are returned governance deposits, IBC refunds, completed
unbonding, and payouts made by the chain itself at the start or end of a
block (for example after a slash). Its balance can go up this way, never
down.

So a contract that pays out should check `isFrozen` first. Otherwise one
frozen payee fails its whole transaction, a batch payout included, at commit
and out of sight of `eth_*`. That is what this precompile is for. All
functions are `view` and cost only the precompile's base read gas.

```solidity
Expand Down Expand Up @@ -139,7 +158,7 @@ Source: `konstellation/x/compliance/precompile/ICompliance.sol` (the ABI is
`abi.json` next to it). Semantics worth knowing:

- Two lists. The **blocklist** (`isFrozen`) is what the chain enforces —
on signers and direct recipients, as above, not on balances; the
on signers, recipients and every balance change, as above; the
**allowlist** (`isVerified`) is opt-in — the chain never requires an
address to be verified to transact, only being frozen stops a tx.
- List changes come from the compliance authority through an on-chain
Expand Down
46 changes: 29 additions & 17 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,23 +101,35 @@ never entered the mempool.
**What it means.** The chain has a compliance block list (`x/compliance`),
checked on every transaction, EVM and Cosmos alike. A transaction is
rejected if a frozen address **signs** it (the sender, a delegation
authority in an EIP-7702 authorization list) or is its direct **`to`** (a
bank send recipient, an EVM `to`). The check runs in the mempool pre-check
as well as the ante handler, so the refusal is immediate and explained
rather than discovered at block time.

**What it does not mean.** A freeze does **not** immobilise the address's
balance. The check looks at signers and the transaction's `to`, not at bank
transfers, and precompiles move balance directly: KASH can still be pulled
out of a frozen account through the werc20 precompile or any contract by an
allowance granted before the freeze (`transferFrom(frozen, spender)`
succeeds), and can still arrive by contract call (`transfer(frozen, …)`
succeeds — only a plain value transfer or bank send *to* it is refused).
Reproduced on a dev chain 2026-09-21. A contract that must not serve a
frozen address has to call
[`isFrozen`](/contracts#icompliance-at-0x0900) itself. A bank-level
restriction is planned before mainnet; until then, do not describe a freeze
as "the funds are locked".
authority in an EIP-7702 authorization list), is its direct **`to`** (a
bank send recipient, an EVM `to`), or is named in a `transfer` /
`transferFrom` on WKASH or another token precompile. The check runs in the
mempool pre-check as well as the ante handler, so the refusal is immediate
and explained rather than discovered at block time.

If a block producer includes such a transaction anyway, it fails in the
block with a different code: `sdk/5 … address is frozen: insufficient
funds`, because fee deduction runs first and the bank restriction refuses
the frozen fee payer. Same cause, and nothing is charged either way. A
refused transaction stays in the node's seen-cache, so resend with new
bytes (a new memo or fee) once the freeze is lifted.

**What it covers.** Nothing can leave a frozen address, by any path. That
includes an allowance granted before the freeze (`transferFrom(frozen, …)`
is refused), a contract, an IBC transfer, authz or a fee grant. No one's
transaction can fund it either. A precompile transfer called from a
contract reverts with `execution reverted: … address is frozen`. A
contract's internal `CALL{value}` to a frozen address is the one quiet case:
the transaction is accepted and gas is charged, then it fails when it
commits and does not show up in `eth_getTransactionReceipt`. Find the reason
with `tx_search` (see
[the no-receipt section](#my-evm-transaction-has-no-receipt-but-i-was-charged-gas)). Contracts that pay
out should check [`isFrozen`](/contracts#icompliance-at-0x0900) first.

**What it does not cover.** Protocol completions may still credit a frozen
address: returned governance deposits, IBC refunds, completed unbonding, and
payouts the chain makes itself at the start or end of a block. Its balance
can rise that way, never fall.

Freezes come from the compliance authority (a foundation multisig on
mainnet) through an on-chain timelock — 24 h by default (governance-changeable,
Expand Down
Loading