From 62d384e46081a146a14cc93f0dc9e355d377f2bf Mon Sep 17 00:00:00 2001 From: Evangel90 Date: Wed, 30 Sep 2026 04:18:15 +0100 Subject: [PATCH] Freeze semantics as built after konstellation #15 (bank-level restriction) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit contracts.md and troubleshooting.md still described a freeze as it worked before konstellation #15 (merged 2026-09-29): "does not immobilise the balance", pre-freeze allowances can drain it, contracts can fund it, "a bank-level restriction is planned". #15 closed that (P20). The docs now say what the chain does: - Nothing can leave a frozen address, by any path: a pre-freeze allowance (refused at submission when a token precompile transfer names it), contracts, IBC, authz, fee grants. No one's transaction can fund it. - A precompile transfer called from a contract reverts with the reason. An internal CALL{value} to or from a frozen address fails at commit, out of sight of eth_* (reason in tx_search); a 0-value call still succeeds. - A frozen contract cannot stake, vote or redirect rewards. - Protocol completions may still credit it (gov deposit refunds, IBC refunds, unbonding, chain payouts at block start/end), so the balance can go up, never down. - troubleshooting: the DeliverTx code (`sdk/5 … insufficient funds`) next to the CheckTx one, and "resend with new bytes" for the seen-cache. Source: konstellation #15's description and README "Compliance (D6) error codes". The ICompliance interface block is unchanged (it is a verbatim copy of the source). `npm run build` is clean (onBrokenLinks: throw, no anchor warnings). Co-Authored-By: Claude Opus 5.5 --- docs/contracts.md | 43 +++++++++++++++++++++++++++----------- docs/troubleshooting.md | 46 ++++++++++++++++++++++++++--------------- 2 files changed, 60 insertions(+), 29 deletions(-) diff --git a/docs/contracts.md b/docs/contracts.md index 73a2bb2..089fd7d 100644 --- a/docs/contracts.md +++ b/docs/contracts.md @@ -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 @@ -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 diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index ec7808b..45fcb32 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -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,