Skip to main content
Version: 0.1.0

Application wiring

This page describes how the five modules are assembled into the application: registration order, what runs in each block, and the three mechanisms the chain uses to connect modules to each other.

:::note Naming x/nft is the Go package; nftrwa is the module name, the store key, and the query prefix. There is no centerchain.v1 core module: the five directories under proto/centerchain/ are income, liquidity, nft, nftmarket, and tokenfactory. :::

Registered modules​

Beyond the standard Cosmos SDK modules, the application registers:

ModuleOn-chain nameStore keyConsensus version
Token factorytokenfactorytokenfactory2
Liquidityliquidityliquidity5
NFTnftrwanftrwa1
NFT marketnftmarketnftmarket1
Incomeincomeincome1

IBC is registered outside the app-config composition path, together with the transfer application, interchain accounts, the 07-tendermint light client, and the 06-solomachine light client. There is no CosmWasm module.

Order matters​

InitGenesis runs tokenfactory before nftmarket, because the market's genesis import reads IsDenomRestricted to fail closed on a restricted denom in a listing price or an accepted-denom list.

The five CenterChain modules run last in all three lifecycle lists, after the standard modules, in the order nftrwa, nftmarket, tokenfactory, liquidity, income.

Block execution​

ModuleBeginBlockEndBlock
nftrwaNo-opNo-op
nftmarketPrunes up to 200 expired listingsNo-op
tokenfactoryNo-opNo-op
liquidityLoads state, activates due fee updates, settles every emission scheduleIn-memory invariants every block; bank reconciliation every 10,000 blocks
incomeNo-opSolvency check every 10,000 blocks

Both invariant checks emit an alarm event and return no error. A violated invariant is therefore observable in the event stream without halting the chain.

How modules connect​

The chain does not use the SDK's SendHooks interface anywhere. It uses three other mechanisms instead.

1. Bank send restrictions​

Two modules append a function to the bank's restriction list:

ModuleCallPosition
incomePrependSendRestrictionRuns first.
tokenfactoryAppendSendRestrictionRuns last.

The order is deliberate: income must checkpoint a holder's accrual against the pre-transfer balance before tokenfactory decides whether the transfer is allowed at all.

2. The NFT transfer policy​

The vendored cosmossdk.io/x/nft fork exposes a package-level transfer policy. tokenfactory installs itself as that policy, so every NFT send is evaluated by the same blacklist and restriction logic as a bank send.

A second ante decorator, NFTSendCheckDecorator, re-checks NFT sends — including ones nested inside an authz.MsgExec, up to eight levels deep — and rejects locked, blacklisted, or unknown-counterparty transfers before they reach the handler.

3. Named burn hooks​

x/nft keeps a named hook registry that runs on every burn in sorted order. Two hooks are registered:

NameRegistered byEffect
incomex/incomeReleases the token's earmarked claimable.
nftmarketx/nftmarketRefunds the listing deposit and clears the listing.

The first hook to return an error aborts the burn and rolls the transaction back.

4. Keeper injection​

The remaining coupling is direct keeper wiring rather than hooks:

  • liquidity registers itself with tokenfactory as the module allowed to create and mint LP denoms, and injects the tokenfactory keeper as its denom eligibility and restriction source.
  • nftmarket injects the tokenfactory keeper to read the restricted-denom rule.
  • income injects the tokenfactory keeper to read authority metadata and the blacklist, and the NFT keeper for the burn hook.
  • liquidity is given an IBC asset-health checker that verifies the transfer module's denom trace, the channel, the connection, and the client state.

5. A store wrapper​

tokenfactory also wraps the bank's KV store service. On any write that decreases a balance, the wrapper checks the swap reservation counters and rejects the write if the new balance would fall below what open swap authorizations have reserved. This is enforced at the store layer, so it holds regardless of which code path performed the transfer.

Ante handler​

The existing ante chain is wrapped by two tokenfactory decorators:

ZeroBalanceCheckDecorator // outer
NFTSendCheckDecorator // inner
... the pre-existing ante chain ...

ZeroBalanceCheckDecorator runs only in CheckTx and simulate, and re-applies CanSendTokens to every MsgSend in the transaction after the rest of the chain has run. Its purpose is to make the result visible in a simulation, not to change consensus behaviour.

Module accounts​

AccountPermissionsBlocked from receiving
tokenfactoryMinter, burnerNo
liquidityMinter, burnerYes
nftNoneYes
nftmarketNoneYes
incomeNoneNo — the yield pool must be able to receive

The liquidity module also creates per-pool accounts on demand: liquidity-reserve-{id}, liquidity-farm-{id}, and liquidity-fee-{id}.

Chain identity​

SettingValue
Bech32 account prefixcenter
Validator prefixescentervaloper, centervalcons
Bond and mint denomucen
Base and display denomucen / cen, symbol CEN, exponent 6

Upgrades​

The upgrade handler runs RunMigrations under the plan name v0.55. The registered migrations are:

ModuleFromToMigration
liquidity12MigrateLegacyState
liquidity23MigrateFarmLockDurationBound
liquidity34MigratePoolLifecycleParams
liquidity45MigratePoolFeeCounters
tokenfactory12MigratePoolPolicyGenesisV2

The tokenfactory migration seeds the three genesis counters — factory denoms, pool policies, and blacklisted addresses — that the state limits depend on.