# 01. Current Architecture: Akash on Cosmos SDK
| | |
|---|---|
| **Document** | 01. Current architecture (Akash on Cosmos SDK) |
| **Doc ID** | AKASH-MIG-01 |
| **Version** | 0.9 (draft for review) |
| **Date** | 2026-08-10 |
| **Owner** | Overclock Labs |
| **Audience** | Vendor engineering |
| **Status** | INFORMATIVE: no `REQ-*` requirements; the factual baseline the normative documents build on |
## Purpose
- Describe, at engineering depth, what the Akash protocol **is today** on its sovereign Cosmos SDK chain: every
module, its state layout, messages, parameters, events, block-lifecycle behavior, and inter-module wiring.
- Give Vendor engineers with **zero Cosmos background** enough context to read the rest of the set: every
Cosmos-specific concept is defined on first use (§1.2).
- Isolate behaviors the target chain must reproduce faithfully (escrow settlement, the BME engine, the close
cascade; §4) from incidental Cosmos plumbing that will not carry over.
- Enumerate off-chain systems coupled to the chain (§6), known defects that must **not** be ported (§7), and data
that exists only as mainnet state and must be pulled at kickoff (§8).
## In scope
- The v2 protocol line as at commit `096bff57` (2026-08-10) of
[`akash-network/node`](https://github.com/akash-network/node), Go module `pkg.akt.dev/node/v2`.
- Custom Akash modules, the in-repo CosmWasm contracts, chain-level configuration, genesis shape, upgrade history,
and off-chain integration seams.
- Behavior descriptions grounded in code, cited as `path/file.go:line`.
## Out of scope
- Anything about the target chains; see [03](./03-solana-architecture.md) and [04](./04-ethereum-architecture.md).
- Migration mechanics; see [05](./05-token-migration.md) and [06](./06-state-and-data-migration.md).
- Message/state/event mapping to target designs; see [14](./14-appendix-protocol-mapping.md).
- Rationale for what is kept vs dropped; recorded as `D-xx` in [13](./13-open-questions-and-assumptions.md).
## 1. Orientation
### 1.1 What Akash is
Akash Network is a decentralized marketplace for compute. **Tenants** (customers) describe workloads in **SDL**
(Stack Definition Language, a YAML manifest: containers, resources, placement constraints, pricing). **Providers**
(datacenter/GPU operators running the separate `provider-services` daemon on Kubernetes) bid for that business.
The chain's job is narrow: run the **order book** (deployments → orders → bids → leases), hold **escrow** so
tenants can pay for leases as they run, meter payment out to providers block by block, and anchor the
identity/attribute/audit records that make bids trustworthy. Workloads never touch the chain: the SDL manifest
travels tenant→provider over an authenticated HTTPS channel, and only its hash lives on-chain.
Today this runs as a **sovereign Cosmos SDK Layer-1** ("Akash mainnet", chain-id `akashnet-2`) with its own
validator set, staking token (AKT), inflation, and governance. This document is the "as-is" baseline the
migration program starts from.
### 1.2 Cosmos SDK primer: concepts used throughout
The Cosmos SDK is a Go framework for building application-specific blockchains on the CometBFT (formerly
Tendermint) BFT consensus engine. Definitions the rest of this document assumes:
| Concept | Definition |
|---|---|
| **Module (`x/name`)** | A vertical slice of on-chain functionality: state + message handlers + queries + genesis logic. Stock SDK modules (`x/bank`, `x/staking`, `x/gov`, …) ship with the framework; Akash adds ten custom ones. |
| **Keeper** | A module's state-access object; holds its KV store and references to other modules' keepers (the dependency graph). "Keeper X calls keeper Y" = synchronous in-process call inside one state transition. |
| **KV store / IAVL / collections** | Each module owns a namespaced key-value store inside one Merkleized tree (IAVL); keys are hand-rolled byte prefixes. `collections` are newer typed wrappers; an `IndexedMap` maintains secondary indexes (e.g. "orders by state") automatically. A **transient store** is per-block scratch, wiped after each block. |
| **Msg / Query** | A `Msg` is a typed, signed state-transition request (protobuf), e.g. `MsgCreateDeployment`; a transaction carries one or more, executed atomically (any error rolls back the tx). A `Query` is a read-only gRPC service per module, also exposed as REST via **grpc-gateway**. |
| **Params** | A module's governance-adjustable configuration, changed via `MsgUpdateParams` that only the governance module account may sign ("gov-gated"). |
| **denom / Coin / DecCoin** | A `denom` is a token denomination string (`uakt` = micro-AKT, 10⁻⁶ AKT). `Coin` = integer amount + denom; `DecCoin` = high-precision decimal amount + denom (prices/rates). |
| **Module account** | A chain-owned account (no private key) holding funds a module controls, e.g. the escrow pool. Permissions (`Minter`, `Burner`, `Staking`) gate supply operations. |
| **bech32 address** | Cosmos address encoding: human-readable prefix + base32 payload. Akash prefixes: `akash` (accounts), `akashvaloper` (validators), `akashvalcons` (consensus keys) (`pkg.akt.dev/go/sdkutil/init.go:26-33`, `app/types/app.go:94`). |
| **ante handler** | An ordered decorator chain pre-validating every transaction before execution: signature checks, fee deduction, sequence increment. |
| **BeginBlocker / EndBlocker / hooks** | Per-module callbacks run automatically at the start/end of every block ("cron inside consensus"); cross-module ordering is significant (§5.5). Hooks are callback interfaces one module registers on another, e.g. escrow notifies market when an account closes (§4.3). |
| **Events** | Key-value attributes (legacy) or typed protobuf events emitted during execution; indexed by nodes, consumed by off-chain indexers; not consensus state. |
| **x/gov** | On-chain proposal/voting. Governance actions execute as Msgs whose `authority` is the gov module account. |
| **x/authz / x/feegrant** | `authz`: generic grants, where account A grants B the right to execute a specific Msg type within limits (used for delegated escrow deposits, §4.1.3). `feegrant`: A pays transaction fees for B (fees only, on Akash). |
| **IBC** | Inter-Blockchain Communication, Cosmos's trust-minimized cross-chain protocol. ICS-20 = the fungible-token-transfer standard over IBC. |
| **CosmWasm / wasmd / `Any`** | A smart-contract VM module (Rust→Wasm) embeddable in an SDK chain; on Akash it exists solely to host the Pyth oracle plumbing (§3.10–3.11). Protobuf `Any` = type-erased container (`type_url` + bytes), how contracts emit native Msgs. |
| **Genesis / zero-height export** | The JSON document a chain starts from; each module defines an export/import shape. A zero-height export re-creates genesis from live state (basis for migration snapshots). |
| **ConsensusVersion** | Per-module state-schema version, incremented with an in-place store migration at chain upgrades. |
| **Upgrade / Cosmovisor** | Coordinated halt-and-restart chain upgrades scheduled by governance at a named height; Cosmovisor swaps binaries. |
### 1.3 Marketplace lifecycle, end to end
Identifiers: a deployment is `(owner, dseq)`; groups add `gseq` (1..N), orders `oseq`, bids the provider address
and `bseq`. `dseq` is **client-chosen**, by convention the current block height at creation (CLI default), a UX
detail preserved per D-09 (Q-12 tracks whether tooling depends on the height correlation).
```mermaid
sequenceDiagram
autonumber
actor T as Tenant wallet
participant BME as x/bme (burn-mint engine)
participant DEP as x/deployment
participant MKT as x/market
participant ESC as x/escrow
actor P as Provider daemon (off-chain)
Note over T,BME: Lease pricing is ACT-denominated. Tenant first swaps AKT for ACT.
T->>BME: MsgMintACT (burn uakt, mint uact)
BME-->>T: uact credited at next mint epoch (queued, oracle-priced)
T->>DEP: MsgCreateDeployment (groups, SDL hash, uact deposit)
DEP->>ESC: AccountCreate(deployment escrow account, deposit)
DEP->>MKT: CreateOrder (one per group)
P->>MKT: MsgCreateBid (price <= order price, collateral deposit)
MKT->>ESC: AccountCreate(bid escrow account = collateral)
Note over MKT: attribute + audit matching against provider registry
T->>MKT: MsgCreateLease (chosen BidID)
MKT->>ESC: PaymentCreate(rate = bid price per block, payee = provider)
MKT->>ESC: AccountClose(losing bid accounts) - collateral refunded
T-->>P: SDL manifest via provider gateway (OFF-CHAIN, mTLS+JWT).
Provider checks SHA-256(manifest) == on-chain Hash
P->>P: schedules workload on Kubernetes
loop while lease active (streaming payment)
Note over ESC: no per-block work - lazy accrual:
owed = rate x (height - SettledAt)
P->>MKT: MsgWithdrawLease
MKT->>ESC: PaymentWithdraw -> settle, pay provider 100% (no protocol fee)
T->>ESC: MsgAccountDeposit (optional top-up, uakt or uact)
end
alt tenant closes
T->>DEP: MsgCloseDeployment
DEP->>ESC: AccountClose -> settle, refund depositors FIFO
ESC-->>MKT: hooks close leases/orders/bids (cascade, §4.3)
else escrow exhausted
ESC-->>MKT: account/payment overdrawn -> hooks close lease
(reason: insufficient funds)
else provider exits gracefully
P->>MKT: MsgLeaseStartReclaim, then MsgCloseBid after window elapses
end
```
Withdraw/close paths in words: providers pull earned funds any time (`MsgWithdrawLease`); tenants close whole
deployments or individual groups; either close triggers final settlement and FIFO refund of unspent deposits; if
funds run out first, the escrow marks itself **overdrawn** and the hook cascade closes the lease with reason
`insufficient funds` (§4.3). Providers can also *reclaim*: a graceful provider-initiated wind-down with an
on-chain notice window (min 1 h, max 720 h) before the bid may close (D-24).
## 2. Protocol baseline: READ THIS FIRST
> **IMPORTANT: the protocol being migrated is the v2 line, not the classic Akash app.**
> The baseline is commit `096bff57` (2026-08-10, module `pkg.akt.dev/node/v2`). It differs materially from
> pre-2025 descriptions of Akash found elsewhere:
>
> 1. **Dual token.** AKT (`uakt`) remains the staking/governance/gas token. **ACT** (`uact`, "Akash Compute
> Token") is a second denom in which **all lease pricing is denominated**; it is **bank-transfer-disabled**
> (`SendEnabled: uact=false`, `cmd/akash/cmd/genesis.go:121-130`) and moves only through module logic.
> 2. **BME (burn-mint engine, `x/bme`).** ACT is created/redeemed against AKT through a queued, epoch-batched
> burn-mint engine with an oracle-priced collateral-ratio circuit breaker (§3.4, §4.2).
> 3. **On-chain oracle.** AKT/USD enters consensus via `x/oracle`, fed exclusively by a CosmWasm-hosted Pyth
> contract pipeline (§3.10–3.11), the only reason CosmWasm exists on the chain (D-22).
> 4. **Epochs scheduler** (`x/epochs`) drives periodic maintenance (oracle pruning).
> 5. **`x/take` is DELETED.** The historic protocol-fee module is out of the app since v2.0.0 (store removed).
> **Providers currently keep 100% of lease payments; there is no protocol take** (§3.12, §4.1.5).
>
> Any protocol change shipped on mainnet before Gate 1 must be folded back into this document (A-12).
## 3. Module-by-module reference
Store-key bytes below are prefixes inside each module's namespaced KV store. All protobuf/state definitions live
**outside this repo** in the shared API module `pkg.akt.dev/go` (pinned `v0.2.14`, `go.mod:51`), import pattern
`pkg.akt.dev/go/node//`; there are no `.proto` files in `akash-network/node` itself.
App wiring at a glance: Akash keepers are Audit, Bme, Cert, Deployment, Epochs, Escrow, Market, Oracle, Provider,
Wasm (`app/types/app.go:120-131`); **Take is not wired**. Keeper construction order encodes the dependency graph
(`app/types/app.go:421-491`): Oracle → Bme(account, bank, oracle) → Escrow(bank, authz, oracle, bme) →
Market(escrow) → Deployment(escrow, oracle, market, authz, bank) → Provider, Audit, Cert, Epochs, Wasm(awasm),
wasmd.
### 3.1 `x/deployment`: store `deployment`, ConsensusVersion 8
**Purpose.** Marketplace entry point. One SDL deployment = a `Deployment` record plus N `Group`s (a group is a
co-placed resource bundle with a price). Creating a deployment opens a deployment-scoped escrow account and one
market `Order` per group. Groups pause/start/close independently; closing the deployment closes the escrow
account, and everything else follows from the hook cascade (§4.3).
**State** (collections; `x/deployment/keeper/keeper.go:63-66`; protos `pkg.akt.dev/go/node/deployment/v1` (IDs,
Deployment, events) and `.../v1beta4` (Group, GroupSpec, Params, Msgs)):
| Object | Key layout | Fields |
|---|---|---|
| `Deployment` | IndexedMap @ `0x11 0x00`; state index @ `0x11 0x02` | `ID{Owner string, DSeq uint64}`, `State` (active \| closed), `Hash []byte` (SDL manifest hash), `CreatedAt int64`, `Reclamation *{MinWindow time.Duration}` |
| `Group` | IndexedMap @ `0x12 0x00`; indexes `groups_by_state`, `groups_by_deployment` @ `0x12 0x03` | `ID{Owner, DSeq, GSeq uint32}`, `State` (open \| paused \| insufficient_funds \| closed), `GroupSpec{Name, Requirements, Resources}`, `CreatedAt`. `ResourceUnit{Resources (cpu/mem/storage/gpu/endpoints), Count uint32, Price DecCoin}` |
| `pendingDenomMigrations` | Map @ `0x13 0x01` | `DeploymentID → math.Int`: AKT→ACT migration scratch, drained by v2.1.0 |
| `Params` | Item | below |
**Messages** (`x/deployment/handler/server.go`):
| Msg | Key fields | Handler behavior (funds movements in bold) |
|---|---|---|
| `MsgCreateDeployment` | `ID`, `Groups []GroupSpec`, `Hash`, `Deposit{Amount, Sources}`, `Reclamation` | `:41-130`: rejects existing ID; validates deposit vs `MinDeposits`; **rejects `uakt` deposits outright** (`:60-62`: AKT enters escrow only via later `MsgAccountDeposit`); reclamation window bounds vs market params; **group `Price` denom MUST be `uact`** (`:93`); `escrow.AuthorizeDeposits` (§4.1.3); stores Deployment + Groups (gseq 1..N); `market.CreateOrder` per group; **`escrow.AccountCreate` pulls the deposit into the escrow module account** |
| `MsgUpdateDeployment` | `ID`, `Hash` | `:132-157`: active-only; hash must differ; updates the SDL manifest hash pointer |
| `MsgCloseDeployment` | `ID` | `:159-177`: active-only; `escrow.AccountClose`; **all further state changes (refunds, lease/order/bid closure) flow through the escrow→market hook cascade** |
| `MsgCloseGroup` | `GroupID` | `:179-201`: group→`GroupClosed`; `market.OnGroupClosed` closes the group's order/bids |
| `MsgPauseGroup` | `GroupID` | `:203-214`: group→`GroupPaused`; `market.OnGroupClosed` |
| `MsgStartGroup` | `GroupID` | `:226-253`: reopens a paused group; `market.CreateOrder` with the deployment's reclamation settings |
| `MsgUpdateParams` | `Authority`, `Params` | `:255-268`: gov-gated |
**Params:** `MinDeposits sdk.Coins`, default `500000uakt, 500000uact`
(`pkg.akt.dev/go/node/deployment/v1beta4/params.go:32-38`); per-denom validation: a deposit in a denom missing
from `MinDeposits` errors. (The `uakt` entry gates top-ups; creation itself is uact-only, above.)
**Queries:** `Deployments`, `Deployment`, `Group`, `Params` (gRPC `akash.deployment.v1beta4.Query`).
**Events:** `EventDeploymentCreated/Updated/Closed`, `EventGroupClosed/Paused/Started`
(`x/deployment/keeper/keeper.go:228-379`).
**Block hooks:** module `EndBlock` delegates to `keeper.EndBlocker`, a **no-op** (`x/deployment/keeper/abci.go:7-9`);
the comment at `x/deployment/module.go:157` still references a deferred AKT→ACT denom migration, which actually
ran as store-migration v7 (`x/deployment/migrate/v7/act.go`: migrates group prices, escrow accounts/payments,
and authz grants between denoms at an oracle rate).
**Deps:** Escrow, Market, Oracle, Authz, Bank, BME (`x/deployment/imports/keepers.go`); inbound hook
`OnBidClosed(gid)` → `OnPauseGroup` (`keeper.go:421-427`).
**Genesis:** `{Params, Deployments: [{Deployment, Groups[]}]}`.
### 3.2 `x/market`: store `market`, ConsensusVersion 9
**Purpose.** The order book. Every open group has an `Order`; providers respond with `Bid`s, each collateralized
by its own escrow account; the tenant matches one bid into a `Lease`, which creates a streaming escrow `Payment`
at the bid price. Market also owns the provider-initiated **reclamation** flow.
**State** (collections; key layout `x/market/keeper/keys/key.go:29-56`; protos `pkg.akt.dev/go/node/market/v1`
(IDs, Lease, events) and `.../v1beta5` (Order, Bid, Params, Msgs)):
| Object | Key layout | Fields |
|---|---|---|
| `Order` | IndexedMap @ `0x11 0x01`; indexes by state `0x11 0x02`, by group+state `0x11 0x03` | `ID{Owner, DSeq, GSeq, OSeq uint32}`, `State` (open \| active \| closed), `Spec GroupSpec` (copied from group), `CreatedAt`, `Reclamation *DeploymentReclamation` |
| `Bid` | IndexedMap @ `0x12 0x02`; indexes by state `0x12 0x03`, by provider `0x12 0x04`, by order+state `0x12 0x05` | `ID{Owner, DSeq, GSeq, OSeq, Provider, BSeq uint32}`, `State` (open \| active \| lost \| closed), `Price DecCoin`, `CreatedAt`, `ResourcesOffer`, `ReclamationWindow *time.Duration` |
| `Lease` | IndexedMap @ `0x13 0x02`; indexes by state `0x13 0x03`, by provider `0x13 0x04` | `ID LeaseID`, `State` (active \| insufficient_funds \| closed \| reclaiming), `Price DecCoin`, `CreatedAt`, `ClosedOn int64`, `Reason LeaseClosedReason`, `Reclamation *{Window, StartedAt int64, Deadline int64, Reason}` |
| `Params` | Item @ `0x14 0x00` | below |
**Messages** (`x/market/handler/server.go`):
| Msg | Key fields | Handler behavior |
|---|---|---|
| `MsgCreateBid` | `ID BidID`, `Price DecCoin`, `Deposit`, `ResourcesOffer`, `ReclamationWindow` | `:29-136`: per-denom `BidMinDeposits`; rejects if order already has > `OrderMaxBids` bids; `BSeq` must be 0; order must accept bids; `order.Price() >= msg.Price`; offer must match group spec; provider must exist; **attribute matching against provider attrs + `audit.GetProviderAttributes`** (self-declared attrs prepended); `escrow.AuthorizeDeposits`; reclamation bounds; stores Bid; **`escrow.AccountCreate(bid escrow)` pulls the collateral**; telemetry `akash.bids` |
| `MsgCloseBid` | `ID`, `Reason` | `:138-192`. Open bid: just close. Active bid: reclamation gate. Lease active with `Reclamation != nil` ⇒ `ErrReclamationNotStarted`; lease reclaiming and `now < Deadline` ⇒ `ErrReclamationWindowNotElapsed`. Then `deployment.OnBidClosed` (pauses group), closes lease/bid/order, **`escrow.PaymentClose` (final settle + payout)** |
| `MsgCreateLease` | `BidID` | `:209-283`: bid, order, group all open; **`escrow.PaymentCreate(lease payment, payee = provider, rate = bid.Price)`**; stores Lease (copies `bid.ReclamationWindow` into `lease.Reclamation.Window`); marks order/bid matched; **closes all other open bids (`lost`) and `escrow.AccountClose` refunds their collateral** |
| `MsgCloseLease` | `ID`, `Reason` | `:285-336`: tenant-initiated; closes lease/bid/order; **`escrow.PaymentClose`**; `deployment.OnLeaseClosed`; **if the group is still open, re-creates a fresh Order (automatic relist)** |
| `MsgWithdrawLease` | `ID` | `:194-207`. **`escrow.PaymentWithdraw`: settle then pay accrued balance to provider** |
| `MsgLeaseStartReclaim` | `ID`, `Reason` | `:338-379`: lease active, `Reclamation != nil`, not already started; sets `StartedAt = height`, `Deadline = blockTime + Window`, state → `reclaiming`; emits `EventLeaseReclaimStarted` |
| `MsgUpdateParams` | | `:381-392`: gov-gated |
**Params** (`pkg.akt.dev/go/node/market/v1beta5/params.go:16-58`): `BidMinDeposit` (legacy single-denom)
`500000uakt`; `BidMinDeposits` `500000uakt, 500000uact`; `OrderMaxBids` 20 (validation cap 500);
`MinReclamationWindow` 1 h; `MaxReclamationWindow` 720 h.
**Queries:** `Orders/Order/Bids/Bid/Leases/Lease/Params`.
**Events:** `EventOrderCreated/Closed`, `EventBidCreated/Closed`, `EventLeaseCreated/Closed`,
`EventLeaseReclaimStarted`. `LeaseClosedReason` enum: `Invalid=0`, `Owner=1`, `Unstable=10000`,
`Decommissioned=10001`, `Unspecified=10002`, `ManifestTimeout=10003`, `InsufficientFunds=20000`.
**Block hooks:** none. **Hooks provided (inbound from escrow):** `OnEscrowAccountClosed`,
`OnEscrowPaymentClosed` (`x/market/hooks/hooks.go`, wired `app/types/app.go:568-574`); see §4.3.
**Deps:** Escrow, Deployment, Provider, Audit, Account, Authz, Bank (`x/market/handler/keepers.go`).
**Genesis:** `{Params, Orders, Bids, Leases}`.
### 3.3 `x/escrow`: store `escrow`, ConsensusVersion 3
**Purpose.** Generic streaming-payment escrow used by deployments (funding accounts) and bids (collateral
accounts). An `Account` holds multi-denom funds plus an ordered depositor list; `Payment`s attached to an account
drain it at a per-block `Rate`. Settlement is **fully lazy**: no per-block sweep (EndBlocker returns nil,
`x/escrow/keeper/abci.go:8-10`). Deep-dive in §4.1.
**State** (raw KVStore, NOT collections; `x/escrow/keeper/key.go:19-26`); note the unusual **state-in-key**
encoding (§4.1.4):
| Object | Key layout |
|---|---|
| `Account` | `0x11 0x00` ‖ state byte (`open=0x01`, `closed=0x02`, `overdrawn=0x03`) ‖ `'/'` ‖ `id.Key()` |
| `Payment` | `0x12 0x00` ‖ same shape |
| `BmeAccountsPrefix` | `0x14 0x01`: declared, unused (dead) |
Legacy `v1beta3` prefixes are retained alongside. Types (protos `pkg.akt.dev/go/node/escrow/{id/v1, types/v1,
v1}` + `types/deposit/v1`):
```
escrowid.Account{Scope (invalid|deployment|bid), XID string} // deployment/bid ID rendered to string
escrowid.Payment{AID Account, XID string} // lease ID rendered to string
AccountState{Owner, State (open|closed|overdrawn), Transferred DecCoins, SettledAt int64 /*height*/,
Funds []Balance{Denom, Amount LegacyDec}, Deposits []Depositor}
Depositor{Owner, Height int64, Source (balance|grant), Balance DecCoin}
PaymentState{Owner, State, Rate DecCoin /*per block*/, Balance DecCoin /*accrued, unwithdrawn*/,
Unsettled DecCoin /*debt when overdrawn*/, Withdrawn Coin}
```
**Messages:** exactly one, `MsgAccountDeposit{Signer, ID, Deposit{Amount, Sources}}`
(`x/escrow/handler/server.go:31-44`), the top-up path and **the only way AKT (`uakt`) ever enters a deployment
escrow** (creation rejects it, §3.1). Everything else is keeper-to-keeper API called by deployment/market.
**Queries:** `Accounts`, `Payments` (`x/escrow/keeper/grpc_query.go`; prefix search over scope/state/xid).
**Params:** none. **Events:** none of its own; state changes surface via deployment/market events triggered by
hooks. **Block hooks:** EndBlocker nil (the settlement sweep was removed). **Deps:** Bank, Authz, Oracle, BME.
**Genesis:** `{Accounts, Payments}`. Recent fix the Vendor should know: `d7d0205d`. Closed payments are
persisted **before** account hooks fire (ordering matters for the cascade).
### 3.4 `x/bme`: stores `bme` + transient `bme`, ConsensusVersion 1
**Purpose.** The burn-mint engine between `uakt` and `uact`. Swap requests are queued as ledger records and
executed in batches on epoch boundaries inside the EndBlocker; a collateral-ratio (CR) circuit breaker halts ACT
minting when the vault's AKT value degrades. The `bme` module account is the vault and the only Akash module
account with `Burner+Minter`. Deep-dive in §4.2. Protos: `pkg.akt.dev/go/node/bme/v1`.
**State** (collections; `x/bme/keeper/key.go:12-26`):
| Object | Key | Content |
|---|---|---|
| `Params` | Item @ `0x09 0x00` | below |
| `status` | Item @ `0x04 0x00` | `{Status MintStatus, PreviousStatus, EpochHeightDiff int64}` |
| `epochs` | Map @ `0x04 0x01` | `"mint"` / `"burn"` → next execution height |
| `remintCredits` | Map @ `0x01 0x00` | denom → Int (§4.2.3) |
| `totalBurned` / `totalMinted` | `0x02 0x01` / `0x02 0x02` | lifetime counters |
| `ledgerPending` | Map @ `0x03 0x01` | queued swaps: `LedgerRecordID{Denom, ToDenom, Source, Height, Sequence}` → `{Owner, To, CoinsToBurn Coin, DenomToMint, Attempts uint32}` |
| `ledger` | Map @ `0x03 0x02` | executed: `{BurnedFrom, MintedTo, Burner, Minter, Burned/Minted *CoinPrice{Coin, Price LegacyDec}, Spread Coin, RemintCreditIssued/Accrued *CoinPrice}` |
| `ledgerPendingBalances` | Map @ `0x03 0x03` | denom → in-flight amount (excluded from CR) |
| `ledgerCanceled` | Map @ `0x03 0x04` | `{Owner, CancelReason, To, CoinsToBurn, DenomToMint}` |
| `ledgerSequence` | transient Item @ `0x03 0x05` | per-block sequence, reset each BeginBlocker |
`MintStatus` enum: `unspecified=0, healthy=1, warning=2, halt_cr=3, halt_oracle=4`.
**Messages** (`x/bme/handler/server.go`):
| Msg | Behavior |
|---|---|
| `MsgBurnMint{Owner, To, CoinsToBurn, DenomToMint}` | `:52-75`: general form → `RequestBurnMint` |
| `MsgMintACT` | `:77-98`. Sugar: burn `uakt` → mint `uact` |
| `MsgBurnACT` | `:100-121`. Sugar: burn `uact` → mint `uakt` (redemption) |
| `MsgFundVault{Authority, Amount, Source}` | `:123-167`: **gov-only**; source must be a non-module account; **bank-sends source → bme vault**; emits `EventVaultFunded` |
| `MsgUpdateParams` | `:34-50`: gov-gated |
`RequestBurnMint` (`x/bme/keeper/keeper.go:786-865`): only `uakt`↔`uact`; requires a healthy oracle price for
both denoms; rejects when status ≥ `halt_cr` **except ACT→AKT redemptions remain allowed under a CR-driven halt**
(blocked under `halt_oracle`); allocates `(height, sequence)`; adds to `ledgerPendingBalances`; **escrows the
burn-side coin into the module account immediately**; writes the pending record. Execution is in the EndBlocker
(§4.2.1).
**Params** (`pkg.akt.dev/go/node/bme/v1/params.go:13-44`): `CircuitBreakerWarnThreshold` 9500 bps;
`HaltThreshold` 9000 bps; `MintSpreadBps` 25; `SettleSpreadBps` 0; `MinEpochBlocks` 10;
`EpochBlocksBackoffPercent` 10; `MaxEndblockerRecords` 50; `MinMint` 10,000,000 uact; `MaxPendingAttempts` 3.
Validation: warn > halt, both ≤ 10000, spreads ≤ 1000. The same file also defines the settlement epoch identifier
`"bme"` and a 1 h oracle TWAP window.
**Queries:** `Params`, `VaultState`, `Status`, `LedgerRecords`.
**Events:** `EventMintStatusChange{Previous, New, CollateralRatio}`, `EventVaultFunded`,
`EventLedgerRecordExecuted`, `EventLedgerRecordCanceled`.
**Block hooks:** BeginBlocker resets the transient sequence; EndBlocker executes epochs
(`x/bme/keeper/abci.go:35-213`, §4.2.1).
**Deps:** Account, Bank (mint/burn/send/`GetSupply`/`GetBalance`), `Oracle.GetAggregatedPrice`.
**Genesis:** `{Params, State{TotalBurned, TotalMinted, RemintCredits}, Ledger{Records, PendingRecords}}`.
### 3.5 `x/oracle`: stores `oracle` + transient `oracle`, ConsensusVersion 2
**Purpose.** The AKT/USD price oracle. Authorized sources (in practice: exactly one CosmWasm Pyth contract
address) push timestamped price entries; the EndBlocker computes a TWAP+median aggregate and a health status.
Consumers: `bme.calculateCR`, `escrow.settleFromAktFallback`, the deployment keeper. Recently moved to time-based
prices (`319be8e8`). Protos `oracle/v2` (`v1` retained for migration).
**State** (collections; `x/oracle/keeper/key.go:12-24`): `Params` Item @ `0x09`; `latestPriceID`
Map[`PriceDataID{Source uint32, Denom, BaseDenom}`] @ `0x11 0x01`; `aggregatedPrices`
Map[`DataID{Denom, BaseDenom}` → `AggregatedPrice`] @ `0x11 0x02`; `pricesHealth` @ `0x11 0x03`; `prices`
Map[`PriceDataRecordID{Source, Denom, BaseDenom, Timestamp, Sequence}` (custom key codec)] @ `0x11 0x05`; source
registry `sourceSequence` @ `0x12 0x00` / `sourceID` Map[string→uint32] @ `0x12 0x02`; transient `pricesSequence`
@ `0x12 0x01`.
**Messages:** `MsgAddPriceEntry{Signer, ID DataID, Price LegacyDec, Timestamp}`; signer must be listed in
`params.Sources`; **only `Denom=="akt"`, `BaseDenom=="usd"` accepted**; price positive; timestamp monotonically
non-decreasing per source. `MsgUpdateParams` gov-gated.
**Params** (`x/oracle` `params.go:31-46`): `MinPriceSources` 1; `MaxPriceStalenessPeriod` 30 s;
`MaxPriceDeviationBps` 150; `TwapWindow` 5 s; `PriceRetention` 24 h; `PruneEpoch` `"hour"`; `MaxPrunePerEpoch`
1000; `MaxFutureTimeDrift` 10 s; `Sources` (mainnet: the Pyth contract address, set by v2.1.0; see §5.8);
`FeedContractsParams []*Any`.
**Queries:** `Prices`, `Params`, `AggregatedPrice`.
**Events:** `EventPriceData`, `EventPriceStaleWarning`, `EventPriceStaled`, `EventPriceRecovered`,
`EventAggregatedPrice`.
**Block hooks:** EndBlocker (`x/oracle/keeper/abci.go:28-176`): builds the active-source set from params (entries
from removed sources ignored); drops entries older than `now − staleness`; computes TWAP over `[now − window,
now]` with boundary clamping; aggregates + writes health; emits `EventAggregatedPrice` only when healthy. Pruning
of records older than `PriceRetention` runs via the epochs hook (`x/oracle/keeper/prune.go`), ≤
`MaxPrunePerEpoch` per `"hour"` epoch.
### 3.6 `x/epochs`: store `epochs`
**Purpose.** Osmosis-style named-epoch scheduler exposing `BeforeEpochStart`/`AfterEpochEnd` hooks. Its only
consumer today is oracle price pruning; BME keeps its own height-based epoch counters (§3.4), though the named
epoch `"bme"` (`DefaultSettlementEpochName`) is defined. Protos `epochs/v1beta1`.
**State:** `EpochInfo{ID, StartTime, Duration, CurrentEpoch, CurrentEpochStartTime, CurrentEpochStartHeight,
EpochCountingStarted}` per named epoch (`"hour"`, `"bme"`). **Messages/Params:** none. **Queries:** `EpochInfos`,
`CurrentEpoch`. **Block hooks:** BeginBlocker (`x/epochs/keeper/abci.go:12-91`); on rollover it emits
`EventEpochEnd`, runs `AfterEpochEnd` hooks in a cache context (**hook errors are swallowed, non-halting**),
increments, emits `EventEpochStart`, runs `BeforeEpochStart` the same way. **Genesis:** `{Epochs}`.
### 3.7 `x/provider`: store `provider`, ConsensusVersion 3
**Purpose.** The provider registry: owner address, public host URI (the gateway tenants talk to), self-declared
attributes (region, tier, GPU models, …), contact info. Bids are validated against these attributes plus audits
(§3.8). Protos `pkg.akt.dev/go/node/provider/v1beta4`.
**State:** raw KV, single map; `ProviderKey = ProviderPrefix ‖ LengthPrefix(ownerAddr)`;
`Provider{Owner, HostURI, Attributes, Info{EMail, Website}}`.
**Messages** (`x/provider/handler/server.go`): `MsgCreateProvider` `:33-51`; `MsgUpdateProvider` `:53-72` (the
historical "no active leases" guard was removed in v0.32.0 for gas cost and never reintroduced);
`MsgDeleteProvider` `:74-88`, which **returns `ErrInternal "NOTIMPLEMENTED"`** (in-code TODO: cancel leases first; §7).
**Queries:** `Providers`, `Provider`. **Params:** none. **Events:** `EventProviderCreated/Updated`
(`EventProviderDeleted` exists in proto, never emitted). **Genesis:** `{Providers}`.
### 3.8 `x/audit`: store `audit`, ConsensusVersion 3
**Purpose.** Third-party attestation over provider attributes. An auditor signs a subset of a provider's
attributes; bid matching treats audited attributes first-class via `audit.GetProviderAttributes` (§3.2). Protos
`pkg.akt.dev/go/node/audit/v1`.
**State:** raw KV; `ProviderKey = PrefixProviderID ‖ LengthPrefix(owner) ‖ LengthPrefix(auditor)`; value
`AuditedAttributesStore{Attributes}`; read model `AuditedProvider{Owner, Auditor, Attributes}`. Recent fix
`151b989a` (serialize audited attributes in queries).
**Messages:** `MsgSignProviderAttributes{Owner, Auditor, Attributes}`;
`MsgDeleteProviderAttributes{Owner, Auditor, Keys []string}`.
**Queries:** `AllProvidersAttributes`, `ProviderAttributes`, `ProviderAuditorAttributes`, `AuditorAttributes`.
**Params:** none. **Events:** `EventTrustedAuditorCreated/Deleted`. **Genesis:** `{Providers}`.
### 3.9 `x/cert`: store `cert`, ConsensusVersion 4
**Purpose.** On-chain x509 certificate registry backing mutual-TLS between tenant clients and provider gateways;
the chain is the CA-less trust root (a certificate is trusted because its owner published it on-chain). Per D-10
this module is **not ported** (replaced by JWT auth over on-chain registered signing keys), but its semantics
matter for the transition window ([07](./07-offchain-and-clients.md)). Protos `pkg.akt.dev/go/node/cert/v1`.
**State:** raw KV; key `0x11` ‖ state (`valid=0x01`, `revoked=0x02`) ‖ `len(owner)` ‖ owner ‖ `len(serial)` ‖
serial (max serial length 40). `Certificate{State, Cert []byte PEM, Pubkey []byte}`; `CertID{Owner, Serial
big.Int}`.
**Messages:** `MsgCreateCertificate{Owner, Cert, Pubkey}`, which parses/validates the x509, stores keyed by serial
(duplicates rejected); `MsgRevokeCertificate{ID}`, which moves the record to the revoked prefix.
**Queries:** `Certificates` (filter Owner/Serial/State). **Params/Events:** none.
**Genesis:** export re-parses every stored x509 and **panics** if the parsed serial mismatches the store key;
relevant to export tooling in [06](./06-state-and-data-migration.md).
Off-chain companions in-repo: `x/cert/utils/key_pair_manager.go` (local cert generation/load) and
`x/cert/utils/utils.go` (`LoadAndQueryCertificateForAccount`: local validity + on-chain presence check). **JWT
auth is off-chain only today:** `akash auth jwt` (`cmd/akash/cmd/auth.go`) signs keyring-backed JWTs via
`pkg.akt.dev/go/util/jwt` (`--exp/--nbf/--access/--scope`); no on-chain JWT state; verification is provider-side.
### 3.10 `x/wasm` ("awasm") + wasmd: stores `awasm` and `wasm`
**Purpose.** `wasmd` is the standard CosmWasm smart-contract module. Akash wires it **only** to host the Pyth
oracle contract pipeline (§3.11) and wraps it with a thin guardrail module `awasm` (store `awasm`,
ConsensusVersion 1, protos `wasm/v1`) that filters what contracts may do.
**awasm state:** `Params{BlockedAddresses []string}`. **Messages:** `MsgUpdateParams` only. **Queries:** `Params`.
**Events:** `EventMsgBlocked{ContractAddress, MsgType, Reason}`.
**The message filter** (`x/wasm/keeper/msg_filter.go:64-180`, installed via `WithMessageHandlerDecorator`,
`app/types/app.go:495-497`) constrains every native message a contract emits: Bank `Send` ALLOW unless the
recipient is blocked; Bank `Burn` DENY; Staking/Distribution/Gov/IBC/IBC2/Custom DENY; Wasm (contract→contract)
ALLOW; protobuf `Any`: **ALLOW exactly one type-url, `/akash.oracle.v2.MsgAddPriceEntry`** (`:171`), the single
pinhole through which Pyth prices reach `x/oracle`; everything else DENY.
**Query side:** the stargate query whitelist is **empty** (`x/wasm/bindings/query_whitelist.go`: all stargate
queries from contracts rejected) and the custom-querier handlers are commented out
(`x/wasm/bindings/custom_querier.go` returns UnsupportedRequest), though the plugin wiring exposes
`AkashQuery::OracleParams` (`app/types/app.go:499-503`). See §7.
**wasmd configuration is hard-coded** (`app/app.go:158-163`): `MemoryCacheSize=100MB`,
`SmartQueryGasLimit=3,000,000`, `ContractDebugMode=true` (in-code comment: "MUST be false in production";
outstanding defect, §7). Capabilities: builtin + `"akash"`.
### 3.11 `contracts/`: in-repo CosmWasm (Rust) crates
**Purpose.** The oracle feed path: an off-chain pusher submits signed Pyth price updates to a CosmWasm contract,
which verifies them and emits the one whitelisted native message (`MsgAddPriceEntry`) into `x/oracle`. Per D-22
this layer collapses into direct Pyth pull-oracle reads on the targets; it is documented because its
**verification semantics** define what "a valid price" means today.
Cargo workspace (Rust 1.86, edition 2021, `panic=abort`, overflow checks; `cosmwasm-std 3.0.2`, `cw-storage-plus
3.0.1`). Contract bytecode is embedded in Go upgrade handlers (`upgrades/software/v2.1.0/contracts.go`,
`.../v2.0.0/contracts.go`; `script/wasm2go.sh`) and uploaded via `MsgStoreAndMigrateContract` during upgrades.
| Crate | Version | Role |
|---|---|---|
| `contracts/pyth_pro` | 1.0.1 | Newest consumer (`0dbfd230`): delegates VAA signature verification to `pyth_vaa` instead of embedding a Wormhole guardian set |
| `contracts/pyth` | 1.0.1 | Original consumer; verifies against the wormhole contract |
| `contracts/pyth_vaa` | 1.0.1 | "Pyth router-signed VAA verifier"; near-stateless verifier queried by `pyth_pro` |
| `contracts/wormhole` | 1.0.0 | Full Wormhole core: guardian sets, VAA archive, governance packets |
`pyth_pro`: `Config{admin, pyth_vaa_contract, update_fee Uint256, price_feed_id, default_data_id{denom:"akt",
base_denom:"usd"}}`; stores `PriceFeed{symbol "AKT/USD", price, conf, expo=-8, publish_time, prev_publish_time}`.
`execute_update_price_feed` (`contracts/pyth_pro/src/contract.rs:90-231`): requires attached `uakt` funds ≥
`update_fee`; parses the Pyth accumulator update (PNAU, from the Hermes service); smart-queries `pyth_vaa`
`VerifyVAA{vaa, block_time}`; verifies the Merkle proof; parses the price-feed message; asserts feed id, non-zero
price, `expo == -8`, `publish_time >=` stored; saves the feed; emits
`CosmosMsg::Any{"/akash.oracle.v2.MsgAddPriceEntry"}` with manual protobuf encoding
(`contracts/pyth_pro/src/oracle.rs:89`). `pyth_vaa` holds a router-verifier config (router set index, router
keys, expected emitter chain/address). `wormhole` maintains guardian sets, sequences, a VAA archive, and
governance packets (ContractUpgrade/GuardianSetUpgrade/SetFee/TransferFee).
### 3.12 `x/take`: dead code, not wired
Historic protocol-fee module: skimmed a percentage of every escrow payout to the community pool. Its store was
deleted at the v2.0.0 upgrade; it is absent from keepers, stores, module lists, and module-account permissions
(`app/types/app.go:120-131`, `app/mac.go:15-27`). Residual code: `SubtractFees(amt)` at
`findRate(denom) = DenomTakeRates[denom] ?? DefaultTakeRate` (%); legacy params `DefaultTakeRate=20`, `uakt=2`
(`pkg.akt.dev/go/node/take/v1/params.go:29-38`). **Net effect today: providers keep 100% of lease payments; the
current chain has no protocol take.** Whether the target chain re-introduces a take is a target-design question
([03](./03-solana-architecture.md)/[04](./04-ethereum-architecture.md)); it is *not* current behavior, and parity
tests ([09](./09-testing-and-verification.md)) must model zero take.
## 4. Deep-dives: behavior the target chain must reproduce faithfully
These three subsections are the heart of the protocol. [14](./14-appendix-protocol-mapping.md) maps each element
to target-chain constructs; D-19/D-20/D-21 fix the porting decisions.
### 4.1 Escrow mechanics (`x/escrow/keeper/keeper.go`)
#### 4.1.1 Lazy settlement math
There is **no per-block escrow work**. Each account records `SettledAt` (the height of its last settlement).
Whenever anything touches the account, `accountSettle` (`:535-604`) runs:
```
heightDelta = currentHeight − account.SettledAt
for each open payment: owed = payment.Rate × heightDelta (per-denom, LegacyDec)
for each overdrawn payment: owed = payment.Unsettled (carried debt, cleared on success)
```
`accountSettleFullBlocks` (`:1282-1333`) transfers `owed` from account funds into each `Payment.Balance`
(accrued-but-unwithdrawn earnings), truncating to integer micro-units at withdrawal. Rates are `DecCoin` per
block; with the 6.5 s target block time (`util/network/network.go:8`) a price of X uact/block ≈ X/6.5 uact/s,
the basis for the per-second conversion fixed in D-21. If funds cannot cover a payment in full, the shortfall
becomes that payment's `Unsettled` debt and the payment goes overdrawn. `SettledAt` advances to the current
height, except when the account is already overdrawn, in which case it is frozen (no further accrual against a
dead account).
#### 4.1.2 Settlement trigger points
Settlement runs **only** at these interaction points (no timer, no sweep):
| Trigger | Notes |
|---|---|
| `AccountClose` | deployment close, losing-bid refund |
| `AccountDeposit` | **only when the account is overdrawn** (top-up rescue path) |
| `PaymentCreate` | lease creation |
| `PaymentWithdraw` | provider withdrawal (`MsgWithdrawLease`) |
| `PaymentClose` | lease/bid close |
| `AccountSettle` (public keeper API) | **no in-repo caller**; exists for external use |
Consequence the Vendor must internalize: **overdrawn detection is event-driven.** A lease whose escrow ran dry at
height h is not observed as overdrawn until someone interacts (typically the provider's periodic withdraw).
Nothing in consensus forces timely detection; D-21 adds a permissionless settle entrypoint on the target chain
precisely because of this.
#### 4.1.3 Deposits: multi-depositor FIFO + authz grants
Deposits carry `Deposit{Amount, Sources}`, `Sources ∈ {SourceBalance, SourceGrant}`. `AuthorizeDeposits`
(`:176-345`) walks sources in order for the single signer:
- `SourceBalance`: spend from the signer's own spendable bank balance, capped at the remainder ⇒
`Depositor{signer, source=balance}`.
- `SourceGrant`: resolve `x/authz` grants of type `/akash.escrow.v1.DepositAuthorization` where the tx signer is
grantee (`authzKeeper.GetGranteeGrantsByMsgType`); rebuild a synthetic msg for just the requested amount
(`:252-282`); `TryAccept(partial=true)` decrements the grant's `SpendLimits` and persists it ⇒
`Depositor{granter, source=grant}`. `DepositAuthorization{SpendLimit Coin (legacy), Scopes []Scope, SpendLimits
Coins}`. Grants are **authz**, not feegrant (feegrant covers tx fees only). Supported grantee msg types:
`MsgAccountDeposit`, `MsgCreateDeployment`, `MsgCreateBid`. Since v0.34.0, only **one** authz depositor per
deployment is supported.
Any unfunded remainder ⇒ `ErrInvalidDeposit` (whole tx reverts). `fetchDepositsToAccount` (`:471-533`) then
bank-sends each depositor's coins into the `escrow` module account, resets negative funds to zero first,
increments `Funds`, and **appends** to the ordered `Deposits` list; deposit order is preserved.
**Spend is FIFO; refunds restore grants.** `deductFromBalance` (`:1211-1280`) consumes depositors oldest-first
per denom, pruning exhausted entries and accumulating per-denom `Transferred` totals. On account close/overdraw,
`saveAccount` (`:1022-1104`) refunds each surviving depositor's remaining balance from the module account, and
**if the depositor was a grant, the refund is credited back to the granter's
`DepositAuthorization.SpendLimits`** (`:1050-1075`). Console's fee-sponsorship product depends on this
restore-on-refund behavior (D-21 preserves it as an explicit delegated-deposit allowance).
#### 4.1.4 Overdrawn encoding
"Overdrawn" is encoded twice, and both encodings are load-bearing:
1. **Negative `Funds`.** When FIFO deduction cannot cover the owed amount, the remainder is subtracted anyway,
driving `Funds[denom]` negative; the negative balance *is* the overdraft marker (`deductFromBalance`,
`:1211-1280`). Later deposits reset negatives to zero before crediting (`:471-533`).
2. **State-in-key.** The record is deleted from its `open`-prefixed key and rewritten under the `overdrawn` state
byte (§3.3). Every read probes all three state keys (`:1335-1375`).
On overdraw: account → `StateOverdrawn`; every attached payment → `StateOverdrawn` with residual debt in
`Unsettled`; accrued balances are paid out (`paymentWithdraw`); remaining depositor balances refunded; hooks fire
(§4.3). Raw writers `SaveAccountRaw/SavePaymentRaw` (`:947-962`) bypass hooks/refunds, used only by the denom
migration.
#### 4.1.5 AKT-fallback settlement, and the no-fee payout
`settleFromAktFallback` (`:609-687`): if an account is overdrawn on `uact` but still holds `uakt`, **and**
`bme.GetMintStatus() >= halt_cr` (the engine is halted, so AKT cannot become ACT), **and** the oracle has a
positive AKT price, then the keeper converts the unsettled ACT debt at the oracle price
(`unsettled_uact / akt_price ⇒ uakt`), deducts from the account's AKT funds, bank-sends escrow → provider
directly in `uakt`, clears the `uact` `Unsettled`, and reopens the payment. Debt is not carried past exhausted
funds. This is the safety valve keeping providers whole in AKT when ACT liquidity is frozen; D-21 ports it.
Payout itself, `paymentWithdraw` (`:1187-1209`): truncate `Payment.Balance` to integer micro-units, bank-send
escrow module account → payment owner (the provider), `Withdrawn += earnings`, `Balance −= earnings`. **No
take/fee deduction anywhere in the path: the provider receives 100%** (§3.12).
### 4.2 The BME engine (`x/bme/keeper/`)
#### 4.2.1 Swap queue and epoch execution
A swap is two-phase. Phase 1 (msg handling, §3.4): validate, escrow the burn-side coins into the vault, record a
pending ledger entry keyed `(denom, toDenom, source, height, sequence)`. In-flight amounts sit in
`ledgerPendingBalances` and are **excluded from the collateral ratio** so queued swaps do not distort the
breaker. Phase 2 is the EndBlocker (`x/bme/keeper/abci.go:35-213`), per block in order:
1. **Burn epoch** (`uact → uakt` redemptions): if `epochs["burn"] ≤ height`, iterate pending burn records,
executing at most `MaxEndblockerRecords`; next epoch at `height + MinEpochBlocks`.
2. **Status update** (`mintStatusUpdate`, `keeper.go:882-946`): recompute CR, transition `MintStatus`, emit
`EventMintStatusChange` on change.
3. **Mint epoch** (`uakt → uact`): if the breaker just reset (previous ≥ `halt_cr`, now ≤ `warning`), recompute
the next epoch first; else if due **and** status healthy/warning, iterate pending mint records; a
post-condition check aborts the walk if CR trips mid-loop.
Each record executes in a `CacheContext` (isolated child state merged only on success): success ⇒ write-through +
move to the executed `ledger`; fatal error ⇒ cancel with reason (refund path); retriable error ⇒ `Attempts++`,
canceled at `MaxPendingAttempts` (`BMCancelReasonMaxAttempts`). Execution mints the target denom at the oracle
price, applies `MintSpreadBps` (25 bps withheld on mint; `SettleSpreadBps` 0), and records
`{Burned, Minted, Spread, RemintCredit*}` as `CoinPrice` pairs (amount + execution price).
#### 4.2.2 Collateral ratio and circuit breaker
`calculateCR` (`keeper.go:742-784`):
```
CR = (vault_uakt − pending_uakt) × (price_AKT / price_ACT) / TotalSupply(uact)
```
A zero/absent oracle price ⇒ `halt_oracle` (everything blocked). Otherwise (bps of fully collateralized):
CR < 9000 ⇒ `halt_cr`; CR < 9500 ⇒ `warning`; else `healthy`. Under `halt_cr`, **ACT→AKT redemptions still
execute** (they raise CR); new mints do not. Under `warning`, epochs stretch:
`EpochHeightDiff = MinEpochBlocks × (1 + backoff%)^steps`, `steps = (warn − cr_bps)/10`, capped at 14,400 blocks
(~26 h at 6.5 s), throttling mint throughput as collateral degrades.
#### 4.2.3 Remint credits and vault funding
Remint credits are supply-accounting bookkeeping (the implementation does true burn+mint; comment at
`x/bme/keeper/keeper.go:64-65`): when AKT is burned on the mint side, the burned amount accrues to
`remintCredits["uakt"]` (`keeper.go:601-617`; accrual tracked for non-ACT denoms only). When an ACT→AKT
redemption later mints AKT, it draws that credit down first; the payout splits into a "remint-issued"
(credit-backed) portion and a freshly-minted portion (`keeper.go:516-541`), so net new AKT supply from the
engine trends to zero. The vault is funded only by governance: `MsgFundVault` (§3.4) and, historically, the
v2.1.0 community-pool transfer (§5.9). Vault seeding on the target chain: D-20 / [05](./05-token-migration.md).
### 4.3 The escrow → market → deployment close cascade
Closing anything in Akash converges on escrow state transitions, which fan back out through hooks
(`x/market/hooks/hooks.go`, wired `app/types/app.go:568-574`). This is the most intricate control flow in the
protocol; target designs must reproduce it exactly (cascade tests in [09](./09-testing-and-verification.md)).
```mermaid
flowchart TD
subgraph ESC["x/escrow state transitions"]
SA["saveAccount: account -> closed / overdrawn
(refund depositors FIFO, restore grants)
keeper.go:1022-1104"]
SP["savePayment: payment -> closed / overdrawn
(pay accrued balance to provider)
keeper.go:1106-1129"]
end
SA -->|"OnEscrowAccountClosed(acc)"| MA{"acc.ID parses as
DeploymentID?"}
SP -->|"OnEscrowPaymentClosed(pmt)"| MP{"pmt.ID parses as
LeaseID and bid active?"}
MA -->|"no: bid collateral account"| STOP1["no cascade
(bid accounts are closed BY market)"]
MA -->|"yes, deployment active"| DC["deployment.CloseDeployment"]
DC --> GS{"acc.State == overdrawn?"}
GS -->|yes| GIF["groups -> GroupInsufficientFunds"]
GS -->|no| GC["groups -> GroupClosed"]
GIF --> PG["per closable group:
deployment.OnCloseGroup +
market.OnGroupClosed"]
GC --> PG
PG --> OC["market closes the group's
open order + open bids"]
MP -->|yes| LC1["market.OnOrderClosed +
market.OnBidClosed"]
LC1 --> LR{"payment overdrawn?"}
LR -->|yes| LIF["OnLeaseClosed(state=insufficient_funds,
reason=InsufficientFunds)"]
LR -->|no| LCL["OnLeaseClosed(state=closed,
reason=Unspecified)"]
LIF --> DEP2["deployment.OnBidClosed(gid)
-> OnPauseGroup"]
LCL --> DEP2
```
Directional summary:
- **Deployment-initiated close** (`MsgCloseDeployment`): deployment calls `escrow.AccountClose` and *nothing
else*; settlement, provider payout, depositor refunds, group closure, order/bid/lease closure all happen
inside the escrow save + hook chain.
- **Funds exhaustion**: any settlement trigger (§4.1.2) can flip the account/payment to overdrawn; hooks close
the lease with `InsufficientFunds` (20000) and mark groups `insufficient_funds` (distinct from `closed`; a
top-up + restart path exists).
- **Market-initiated closes** (`MsgCloseBid`/`MsgCloseLease`) call `escrow.PaymentClose`/`AccountClose`
themselves, then do their own order/bid bookkeeping; the hook still fires but finds the bid already inactive
(idempotence matters). `MsgCloseLease` on a still-open group **relists** a fresh order (§3.2).
- Ordering subtlety: closed payments are persisted before account hooks run (`d7d0205d`); hook handlers observe
consistent payment state.
## 5. Chain-level configuration
### 5.1 Tokens and denominations
Denom constants: `pkg.akt.dev/go/sdkutil/init.go:8-34`.
| Family | base | mid | display | exponent | Notes |
|---|---|---|---|---|---|
| AKT | `uakt` | `makt` | `akt` | 6 | staking bond denom (`sdkutil/init.go:21`), gas fees, gov deposits |
| ACT | `uact` | `mact` | `act` | 6 | lease pricing denom; **`SendEnabled=false`** (`cmd/akash/cmd/genesis.go:121-130`); moves only via module logic (escrow, BME) |
| USD | `uusd` | `musd` | `usd` | 6 | oracle quote unit only, not a bankable asset |
Genesis bank metadata (`cmd/akash/cmd/genesis.go:224-265`) defines AKT correctly; ACT's `Display` is `uact`
instead of `act` (defect, §7). D-19 ports ACT with equivalent non-transferability; D-03/D-04 fix 6 decimals for
AKT on the targets to match `uakt` micro-units 1:1.
### 5.2 Consensus, block time, tx processing
- CometBFT consensus, ~**6.5 s average block time**, the constant used in lease/deployment time math
(`util/network/network.go:8` `AverageBlockTime = 6500ms`); first-init node default `TimeoutCommit = 5s`
(`util/cli/configs.go:324`). Per-block escrow rates convert to per-second at this basis under D-21.
- **Ante chain is entirely stock SDK** (`app/ante.go:46-58`): SetUpContext, ValidateBasic, TxTimeoutHeight,
ValidateMemo, ConsumeGasForTxSize, DeductFee (+feegrant), SetPubKey, ValidateSigCount, SigGasConsume,
SigVerification, IncrementSequence. No custom fee or deposit decorators; the constructor's
GovKeeper/FeegrantKeeper non-nil checks (`app/ante.go:38-44`) are vestigial.
- **Unordered transactions are ENABLED** (`authkeeper.WithUnorderedTransactions(true)`, `app/types/app.go:279`):
txs may commit without strict per-account sequence ordering; client and indexer tooling must not assume
monotonic sequences.
- `PrepareProposal`/`ProcessProposal` are deliberate no-ops (`app/app.go:265-270`); no custom mempool, no vote
extensions.
- **Fees:** flat min-gas-price, no fee market (no EIP-1559 analogue); set via `baseapp.SetMinGasPrices`
(`cmd/akash/cmd/app_creator.go:98`). Defaults disagree in-tree: `0.0025uakt` (`cmd/akash/cmd/config.go:26`) vs
`0.025uakt` (`util/cli/configs.go:361`); the community-accepted mainnet floor is `0.025uakt` (§7).
### 5.3 Staking / slashing / gov / distribution parameters
From `MainnetGenesisParams()` (`cmd/akash/cmd/genesis.go:219-299`), the only in-repo statement of intended
values; live values must be read from mainnet state (§8):
| Module | Params |
|---|---|
| staking | UnbondingTime 14 days; MaxValidators 100; BondDenom `uakt`; MinCommissionRate 0.05 |
| distribution | CommunityTax 0; WithdrawAddrEnabled true |
| gov | MinDeposit 2,500,000,000 uakt (2,500 AKT); MaxDepositPeriod 14 days; VotingPeriod 3 days; Quorum 0.2; Threshold/Veto = SDK defaults |
| crisis | ConstantFee 500,000,000,000 uakt |
| slashing | SignedBlocksWindow 30,000 blocks; MinSignedPerWindow 0.05; DowntimeJailDuration 1 min; SlashFractionDoubleSign 0.05; SlashFractionDowntime 0 (no liveness slashing) |
Extras: `ExpeditedMinDeposit` only in the testnetify path (`150000000uakt`, `app/testnet.go:290`); testnet
overrides at `cmd/akash/cmd/genesis.go:301-316`. **gov `MaxMetadataLen` is raised to 10,200** (vs SDK default
256; `app/types/app.go:377-379`); off-chain proposal tooling depends on embedding large metadata. The legacy gov
router with the paramproposal route is still registered (`app/types/app.go:366-375`).
**Mint/inflation:** stock `x/mint` (`app/types/app.go:355-363`; module `app/modules.go:85-91`, inflation-fn
override explicitly nil at `:89`). **Inflation parameters are NOT in this repo**: `GenesisParams.MintParams` is
declared but never assigned (`cmd/akash/cmd/genesis.go:210`); live values are mainnet state only (§8, Q-19).
**Vesting:** stock `x/auth/vesting` (Base/Delayed/Continuous) via `akash genesis add-account`
(`cmd/akash/cmd/genaccounts.go:24-110`); no custom types; the live inventory is mainnet state (§8).
### 5.4 Module accounts, permissions, blocked receivers, stores
`app/mac.go:15-27`:
| Module account | Permissions |
|---|---|
| `fee_collector`, **`escrow`** (the escrow pool), `distribution` | none |
| **`bme`** (the BME vault) | **Burner, Minter** |
| `mint` | Minter |
| `bonded_tokens_pool` / `not_bonded_tokens_pool` | Burner, Staking |
| `gov` | Burner |
| `transfer` (IBC) | Minter, Burner |
`allowedReceivingModAcc` is **empty** ⇒ every module account is blocked from receiving external bank sends
(`app/app.go:85,496-504`); funds enter module accounts only through module logic. The balances these accounts
hold at snapshot are exactly the funds routed to the Wind-down Reserve under D-05
([05](./05-token-migration.md)).
**KV stores** (`app/types/app.go:605-637`); cosmos: consensus, auth, feegrant, authz, bank, staking, mint,
distribution, slashing, gov, params, ibc, upgrade, evidence, ibctransfer, wasm; akash: epochs, escrow,
deployment, market, provider, audit, cert, awasm, oracle, bme. Transient: params, bme, oracle. Legacy `x/params`
subspaces are retained for the stock modules plus deployment/market key tables (`app/types/app.go:582-603`);
other Akash modules store params under key `0x01` in their own stores.
**Cross-module hooks** (`app/types/app.go:554-579`): staking → distribution + slashing (stock); gov → empty
multihook; **escrow → market** (§4.3); **epochs → oracle** (price pruning, §3.5). Hazard: gov routes are
registered before `SetupHooks()` runs (§7 item 6).
### 5.5 Module ordering constraints, and why
Block-lifecycle ordering is computed from declared constraints via `util/partialord` (`app/app.go:325-395`), not
hand-listed. The declared partial order:
- **BeginBlockers:** `epochs` FIRST (epoch rollovers precede everything consuming epoch hooks);
`distribution → slashing → evidence → staking` (stock reward/penalty pipeline); `staking → ibc`; **`oracle`
before `bme` before `escrow`**; `awasm` before `wasm`; `transfer` before `wasm`.
- **EndBlockers:** `gov`, `staking` first (stock); the same oracle→bme→escrow chain governs the modules'
end-of-block work.
- **InitGenesis** is an explicit list ending `… epochs, bme, escrow, awasm, wasm` (`app/app_configure.go`).
The `oracle → bme → escrow` chain is the protocol's economic dataflow, and it is why ordering is semantics, not
style: the oracle EndBlocker publishes the aggregated AKT/USD price; BME's EndBlocker consumes that price to
execute queued swaps and recompute the circuit breaker; escrow's AKT-fallback settlement (§4.1.5) consumes both
the price and BME's halt status. Reordering changes which price a given block's mints and settlements see. On
target chains with no block-lifecycle hooks, the same constraint re-materializes as "read the freshest oracle
price inside the swap/settle instruction/function"; see
[03](./03-solana-architecture.md)/[04](./04-ethereum-architecture.md).
### 5.6 Discovery service: the client/server version contract
The node exposes an Akash-specific discovery endpoint: gRPC `akash.discovery.v1.Discovery/GetInfo`, REST
`GET /akash/discovery/v1/info`, and CometBFT JSON-RPC method `"akash"` (`app/app.go:538-541,570-578`). Payload:
`chain_id`, `node_version`, **`min_client_version`**, and per-API module version maps (deployment `v1beta4`,
market `v1beta5`, oracle `v2`, …). This is a hard client/server contract: `chain-sdk` and the `akt` CLI negotiate
API versions from it (`pkg.akt.dev/go/node/client/README.md`; `testutil/network/rpc.go:12,26` exists solely to
satisfy `aclient.DiscoverClient` in tests). Any target-chain RPC/indexer stack must provide an equivalent
capability/version negotiation surface ([07](./07-offchain-and-clients.md)).
### 5.7 IBC surface
Wired at `app/types/app.go:336-551`:
- IBC core with the 07-tendermint light client only.
- **Token transfer (ICS-20) only.** v1 router with `transfer` and `wasm` ports, **plus the v2 router (IBC
"Eureka")**: `transferv2.NewIBCModule` + a wasm prefix route (`app/types/app.go:546-551`).
- **Absent:** ICA host/controller (added v0.18.0, removed v0.20.0), packet-forward-middleware, ICS-29 fee
middleware, callbacks, async-icq, capability module.
- `ibctransfer` module account holds Minter+Burner (voucher mint/burn; `app/mac.go:25`).
Implication: cross-chain AKT exists as ICS-20 vouchers on other Cosmos chains (Osmosis, Cosmos Hub, …), backed by
`uakt` locked in per-channel IBC escrow addresses on Akash. Those balances are snapshot inputs (D-07, §8,
Q-03/Q-19). Naming trap: "Hermes" in `_run/node` scripts is the **Pyth Hermes price service**, not the IBC
relayer of the same name (`_docs/pyth-integration.md:304-305`).
### 5.8 Hard-coded addresses and trust roots
- `akash1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrqyagled`: the Pyth price contract; baked into the
v2.1.0 upgrade (`upgrades/software/v2.1.0/upgrade.go:66`) and set as the **sole** authorized oracle source
(`oracle.Params.Sources`, `MinPriceSources=1`). **Mainnet price truth is a single-source trust root today.**
- `akash10d07y265gmmuvt4z0w9aw880jnsr700jhe7z0f`: the gov module account; admin of the Pyth/Wormhole contracts
and the `authority` on every `MsgUpdateParams`/`MsgFundVault`.
- Wormhole guardian-set updates bypass Akash governance: any account may submit a valid guardian VAA
(`guardian_set_expirity = 86400`; `_docs/governance-updates.md:44-57`).
### 5.9 Upgrade history
Upgrades self-register via a plugin registry (`upgrades/types/types.go`; installed `app/upgrades.go:34-56`;
height patches run in BeginBlocker `app/app.go:446-454`, currently none). Convention:
`_docs/adr/adr-001-network-upgrades.md`. Only v2.1.0 remains in-tree; history per CHANGELOG + git:
| Upgrade | One-line summary |
|---|---|
| akash_v0.15.0 | Baseline of the named-upgrade era (Cosmos SDK v0.44.x) |
| v0.18.0 | Added Interchain Accounts (ICA) |
| v0.20.0 | Removed ICA |
| v0.24.0 | v1beta3 stores; GPU resource units; multi-denom `MinDeposits`; "Take Pay" introduced (added `take`, `agov`, `astaking`, feegrant) |
| v0.26.0 | Incremental release (see `upgrades/CHANGELOG.md`) |
| v0.28.0 | Added `ResourcesOffer` to bids |
| v0.30.0 | Incremental release (see `upgrades/CHANGELOG.md`) |
| v0.32.0 | Dropped provider active-lease check on update (gas cost) |
| v0.34.0 | Authz deposit-grant reuse (refund restores spend limit) |
| v0.36.0 | Feegrant ante fix |
| v0.38.0 | AEP-61 key-layout performance work |
| v1.0.0 | Cosmos SDK 0.47; deleted custom `astaking`/`agov` |
| v1.1.0 | Repaired overdrawn escrow accounts |
| v1.2.0 | deployment+market state → collections IndexedMap |
| v2.0.0 | Added epochs/oracle/awasm/wasm/bme stores (dual-token protocol); **deleted `x/take` store**; release later retracted in go.mod (superseded by v2.1.0) |
| v2.1.0 | Current (`upgrades/software/v2.1.0/upgrade.go:54-138`): store migrations (oracle v1→v2 **wipes the oracle store**; deployment v7 drains `pendingDenomMigrations`; market v8 no-op); stores+migrates the Pyth contract (admin `AllowNobody`); sets oracle `Sources`; akashnet-2-gated move of 427,414,453 uakt distribution→escrow module account (manual `FeePool.CommunityPool` decrement); backfills `bme.MaxPendingAttempts=3` and market reclamation windows |
The testnetify/upgrade-test tooling (`akash in-place-testnet`, `tests/upgrade`, Cosmovisor v1.7.1) is the current
chain's operational muscle for coordinated upgrades, context for the sunset upgrade in
[10](./10-rollout-and-cutover.md) (D-18).
### 5.10 Genesis export shape (per custom module)
Zero-height export (`app/export.go`; folds validator dust into the community pool at `:151-157`) is the basis
for migration snapshots ([06](./06-state-and-data-migration.md)):
| Module | Export shape |
|---|---|
| deployment | `{Params{MinDeposits}, Deployments: [{Deployment, Groups[]}]}` |
| market | `{Params, Orders, Bids, Leases}` |
| escrow | `{Accounts, Payments}` (no params) |
| provider | `{Providers}` |
| audit | `{Providers: []AuditedProvider}` |
| cert | `{Certificates: [{Owner, Certificate}]}`; export re-parses x509s, panics on serial mismatch |
| oracle | `{Params, GenesisSourceID, GenesisLatestPricesIDs}` |
| bme | `{Params, State{TotalBurned, TotalMinted, RemintCredits}, Ledger{Records, PendingRecords}}` |
| epochs | `{Epochs}` |
| awasm | `{Params{BlockedAddresses}}` |
| take | `{Params}`; not wired, not exported |
### 5.11 Forked core dependencies: a standing maintenance burden
All three foundational dependencies are **Akash forks** (go.mod replaces):
| Upstream | Fork |
|---|---|
| cosmos-sdk v0.53.6 | `github.com/akash-network/cosmos-sdk v0.53.7-akash.2` |
| cometbft v0.38.21 | `github.com/akash-network/cometbft v0.38.21-akash.1` |
| gogoproto v1.7.2 | `github.com/akash-network/gogoproto v1.7.0-akash.2` |
Plus: ibc-go/v10 v10.5.0 (IBC v2/Eureka-capable), wasmd v0.61.7 + wasmvm/v3 v3.0.2, store v1.1.2, iavl v1.2.6,
rosetta v0.50.12 (wired `cmd/akash/cmd/root.go:79`), Go 1.26.2. Maintaining a sovereign chain currently means
rebasing three forks against upstream security releases indefinitely, one of the operational-burden drivers
quantified in [00](./00-executive-summary.md) and [02](./02-target-selection.md).
## 6. Off-chain seams
Everything here keeps working only if the target chain (or its indexer/API layer) preserves the corresponding
contract. Adaptation is specified in [07](./07-offchain-and-clients.md).
| Consumer | Seam into the chain |
|---|---|
| **`pkg.akt.dev/go`** (successor to `akash-api`; pinned v0.2.14, `go.mod:51`) | The shared contract: ALL proto/state types, denom constants, params, `sdkutil` encoding, event helpers, discovery registry. Every Go client of Akash imports it. Replacing the chain starts with replacing/forking this module. |
| **`pkg.akt.dev/go/cli`** (v0.2.4, `go.mod:52`) | The entire `query`/`tx`/`keys`/`events`/`genesis` CLI tree and flag definitions: the node repo has **no** `x/*/client/cli` packages; the `akash` binary (`cmd/akash/cmd/root.go:69-87`) mounts this external module. |
| **`pkg.akt.dev/go/sdl`** (v0.2.2, `go.mod:53`) | SDL parsing/validation used by node tests and provider tooling. |
| **provider-services** (`akash-network/provider`) | The provider daemon: consumes market events (bid engine), x/cert certificates, JWTs, and the discovery endpoint; dev seams at `_run/common-commands.mk:49-84` (auth-server, send-manifest, `--auth-type` JWT) and `_docs/development-environment.md:28-57`. |
| **chain-sdk** (`@akashnetwork/chain-sdk`, TypeScript) | Requires an RPC client exposing the `Akash(ctx)` discovery method (§5.6; `testutil/network/rpc.go`). |
| **Indexers / Console** | grpc-gateway REST + typed events + the `cosmossdk.io/schema` state-streaming seam: newer modules implement `ModuleCodec()` (`x/bme/module.go:190-193`, `x/oracle/module.go:201`, `x/epochs/module.go:166`). **Partial coverage**: only collections-based modules; escrow/provider/audit/cert (raw KV) do not stream, so indexers combine event-tailing with query polling. |
| **`akash-network/net`** repo | Chain metadata contract: `https://raw.githubusercontent.com/akash-network/net/master/mainnet/meta.json` (and `sandbox-2/meta.json`); RPC endpoints, binary URLs, upgrade matrix consumed by installers and tooling. |
| **snapshots.akash.network** | Node bootstrap: `https://snapshots.akash.network//latest`. |
| **Pyth Hermes price-feeder** | Off-chain pusher (`_run/node/price-feeder.sh`, `_docs/pyth-integration.md`): polls Pyth's Hermes service for signed AKT/USD updates and submits `execute_update_price_feed` txs to the CosmWasm contract (§3.11), the component that keeps the oracle alive. Its liveness is a protocol dependency (no price ⇒ `halt_oracle` ⇒ BME and ACT-mint paths freeze). |
### 6.1 SDL and manifest hashing (ADR-002): byte-identical requirement
The SDL YAML never touches the chain. The tenant's client compiles it into a **manifest**; the chain stores only
`Deployment.Hash`; the manifest goes tenant → provider over the provider's gateway, and the provider recomputes
the hash and rejects mismatches. Per ADR-002 (`_docs/adr/adr-002-manifest-v2beta2.md`): **manifest version =
SHA-256 of the sorted JSON serialization** (reference implementation in `akash-api`
`go/manifest/v2beta2/manifest.go`), with strict validation rules (sorted services, name regexes, …).
Consequence: any client producing manifests must serialize **byte-identically** to the Go reference, or providers
reject valid deployments. This constraint survives the migration untouched (A-10, D-09); only the location of
the on-chain hash changes.
### 6.2 Events and the in-process bus
Off-chain consumers key on two event styles: legacy `akash.v1` message events (`sdkutil.EventTypeMessage` with
`BaseModuleEvent{Module, Action}`; `pkg.akt.dev/go/sdkutil/event.go`) and typed protobuf events (e.g.
`bme.EventVaultFunded`). In-process, `pubsub/bus.go` is the async fan-out bus that historically backs the
provider bid engine; on the target chain this maps to websocket/log subscriptions
([07](./07-offchain-and-clients.md)).
## 7. Known defects: do NOT port
Faithful reproduction (§4) does not extend to bugs. Treat the following as explicitly outside the parity
envelope; each is tracked to a fix or design change in the target docs:
1. **`GenesisParams.MintParams` / `.ConsensusParams` declared but never populated**
(`cmd/akash/cmd/genesis.go:210`): `prepare-genesis` emits zero-valued mint params and nil consensus params.
(Moot on targets: no sovereign genesis; live values pulled per §8.)
2. **`wasmConfig.ContractDebugMode = true` hard-coded** (`app/app.go:158-163`) despite the in-code "MUST be false
in production" comment.
3. **Min-gas-price default disagreement**: `0.0025uakt` (`cmd/akash/cmd/config.go:26`) vs `0.025uakt`
(`util/cli/configs.go:361`).
4. **ACT bank metadata `Display: uact`** (should be `act`; `cmd/akash/cmd/genesis.go:224-265`); fix in target
token metadata (D-19).
5. **`upgrades/CHANGELOG.md` consensus-version table is stale** (wrong versions for deployment and oracle; still
lists the removed take module): not authoritative; this document and the code are.
6. **Gov-route/hook ordering hazard**: gov routes are registered before `SetupHooks()` (`app/app.go:188-193`),
so a gov proposal that triggers a hook can nil-deref.
7. **`MsgDeleteProvider` returns `NOTIMPLEMENTED`** (`x/provider/handler/server.go:74-88`): the target provider
registry needs a real deregistration path (with lease safety), not a stub.
8. **No invariants registered**: the crisis module's keeper/store are wired (ConstantFee 500,000 AKT) but its
AppModule is not in `appModules()`, so invariant checks never run; supply/escrow conservation is unverified
on-chain today. Target designs add explicit conservation checks ([09](./09-testing-and-verification.md)).
9. **CosmWasm query surface dead.** Empty stargate whitelist and commented-out custom querier
(`x/wasm/bindings/query_whitelist.go`, `custom_querier.go`): contracts cannot query chain state at all.
Harmless today (the Pyth contracts don't need it); has no successor (D-22).
## 8. Data that exists only on mainnet: pull at Vendor kickoff
The repo does not contain these; they are live `akashnet-2` state, captured in the kickoff data pull
(owner/timing per Q-19; feeds [05](./05-token-migration.md) supply accounting and Q-01 emissions modeling):
1. **Inflation/mint parameters and current annual provisions**: stock `x/mint` state; not in source (§5.3).
2. **Vesting account inventory**: stock Base/Delayed/Continuous vesting accounts
(`cmd/akash/cmd/genaccounts.go:24-110`); enumerate owners, schedules, remaining locked amounts from a state
export (D-06 re-creates them on the target chain).
3. **Supply split by category**: liquid / bonded / unbonding / community pool / module-account balances (escrow,
bme vault, gov deposits, fee collector) / IBC-out; the S1 conservation check in
[05](./05-token-migration.md) reconciles against this.
4. **State sizes per module**: record counts and byte sizes for every store in §5.4 (sizes the export/transform
pipeline and archive plan in [06](./06-state-and-data-migration.md)).
5. **Transaction-rate and operation-mix statistics**: msgs/day by type (deployments, bids, leases, withdrawals,
BME swaps), peak rates; drives target-chain fee/compute budgeting in
[03](./03-solana-architecture.md)/[04](./04-ethereum-architecture.md) and load targets in
[09](./09-testing-and-verification.md).
6. **Community pool balance** (post the v2.1.0 427,414,453 uakt withdrawal, §5.9): Wind-down Reserve input
(D-05).
7. **IBC escrow balances per channel**: `uakt` locked behind each ICS-20 channel, with counterparty chain
identification (Osmosis, Cosmos Hub, …); input to the voucher-return campaign (D-07, Q-03, Q-19).
8. **Current on-chain param values for every module in §3/§5.3**: governance has changed params since genesis;
the tables above are code defaults and intended values, not necessarily live values. The kickoff pull
re-baselines [14](./14-appendix-protocol-mapping.md).
## Cross-references
- [00. Executive summary](./00-executive-summary.md): migration drivers this baseline motivates.
- [02. Target selection](./02-target-selection.md): option analysis built on §2 and §5.11.
- [03. Solana architecture](./03-solana-architecture.md) / [04. Ethereum architecture](./04-ethereum-architecture.md): target re-designs of §3–§4.
- [05. Token migration](./05-token-migration.md): consumes §5.1, §5.4, §8.
- [06. State & data migration](./06-state-and-data-migration.md): consumes §5.10, §8.
- [07. Off-chain services & clients](./07-offchain-and-clients.md): consumes §6.
- [09. Testing & verification](./09-testing-and-verification.md): parity envelope = §4 minus §7.
- [13. Open questions & assumptions](./13-open-questions-and-assumptions.md): D/A/Q items cited throughout.
- [14. Appendix: protocol mapping](./14-appendix-protocol-mapping.md), element-by-element mapping of §3.
## Feeds into
Every design document treats this file as the factual baseline: 03/04 (target architectures re-implement §3–§4
under D-19..D-24), 05/06 (migration of §5.1/§5.4/§5.10 state), 07 (adaptation of §6), 08 (threat-model delta
from §5.8), 09 (behavior-parity test envelope from §4, excluding §7), and 14 (mapping tables enumerate §3
exhaustively). Update this document first (per A-12) whenever mainnet ships a protocol change before Gate 1.