Conversation
A repeated string param, empty by default, that governance sets through MsgUpdateParams. Validate accepts at most 32 well-formed, distinct denoms and never the native denom (halting it would halt every market and pool). Params.IsDenomHalted is an exact string comparison, so market and pool ids that merely contain a denom never match. No migration and no consensus version bump: a new proto3 repeated field decodes as empty from the params bytes already stored on chain. The CI test script now also selects tradebin's v2types package, which the previous pattern (paths ending in /types) silently skipped.
A market or pool whose base or quote is halted now rejects MsgCreateMarket, MsgCreateLiquidityPool, MsgCreateOrder, MsgFillOrders, MsgAddLiquidity and any MsgMultiSwap route touching it (also as an intermediate hop) with the new ErrDenomHalted (4017), before any fee is captured or funds are escrowed. MsgCancelOrder and MsgRemoveLiquidity are untouched: exits always work. The no-swap invariant is enforced at Keeper.swapTokens, the single choke point every swap goes through, so user routes, ModuleSwapForNativeDenom, ModuleAddLiquidityWithNativeDenom and the fee payer all refuse a halted pool at once. HasLiquidityWithNativeDenom, HasDeepLiquidityWithNativeDenom and CanSwapForNativeDenom answer false for a halted denom, so the ante handler refuses it as a fee denom and the fee collector and burner classify it as non-swappable without attempting a swap. The fee payer falls back to capturing the native fee when the preferred denom's pool is halted. Nothing changes in txfeecollector, burner or rewards.
The processing engine asks the keeper whether a message's market is halted before dispatching it. A buy, sell, fill_buy or fill_sell on a halted market is refunded in full to its owner through the existing refundMessageFunds (the cancel coin math, dust stored), emits the new QueueMessageRefundedEvent with reason "market_halted" and leaves the queue like any processed message; cancels are processed unchanged. Gov's EndBlock runs before tradebin's, so a passed halt proposal already applies to the queue in that block, and any backlog beyond order_book_per_block_messages is refunded over the following blocks. Resting orders and aggregates are never touched.
Drives MsgUpdateParams through the real msg service router: the gov authority sets and clears halted_denoms, a non-authority signer and a list naming the native denom are rejected, and the stored list survives a genesis export/import. Documents the param, the effects of a halt and the governance runbook in the tradebin docs.
… as before Revised on review: the order book queue is only the asynchronous execution stage of messages the msg server already accepted (validated, fee captured, funds escrowed), not a second gate. The msg-server guards stop any new message from entering the queue once a halt is in effect, so the few messages queued in the previous blocks can safely execute as always and the queue simply drains. The audited engine stays as it is. Drops the halted-market refund branch from the processing engine, the IsMarketHalted requirement on ProcessingKeeper, the QueueMessageRefundedEvent proto and generated code, and the engine test. Docs and changelog now say already-queued messages execute normally.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Gives BZE a generic, governance-controlled way to halt a denom on the DEX. Once a denom is listed in the new tradebin
halted_denomsparameter, no new order book position and no AMM swap involving it can be opened, while users keep every exit (cancel orders, remove liquidity, bank sends, IBC transfers). The list ships empty in v8.2.0; a denom is halted later by aMsgUpdateParamsgovernance proposal. No migration, noConsensusVersionchange, no change underapp/upgrades, nothing outsidex/tradebin(plus one line in the CI test script, see below). The EndBlock order book engine is untouched.Ticket: BZE-144 (chain release epic BZE-63). UI counterpart: BZE-145.
Behaviour once a denom is halted
A market or pool is halted iff its base or quote is halted (exact string match; the native denom can never be listed).
ErrDenomHalted(4017) before any fee capture or escrow:MsgCreateMarket,MsgCreateLiquidityPool,MsgCreateOrder,MsgFillOrders,MsgAddLiquidity, and anyMsgMultiSwaproute touching the denom, also as an intermediate hop.Keeper.swapTokens, the single choke point of every swap (user routes,ModuleSwapForNativeDenom,ModuleAddLiquidityWithNativeDenom, the fee payer), refuses a pool holding a halted denom.HasLiquidityWithNativeDenom,HasDeepLiquidityWithNativeDenomandCanSwapForNativeDenomanswer false, so the ante handler refuses the denom as a fee denom and the fee collector / burner classify it as non-swappable without attempting a swap.MsgCancelOrder,MsgRemoveLiquidity, bank sends, IBC transfers, staking / rewards that merely hold the denom,MsgFundBurner. Resting orders and pool reserves are left exactly as they are (no sweep).Intended consequences (decided, not side effects)
MsgFundBurner) are treated like any non-swappable IBC denom and end up locked in the burner black hole. Nothing keeps such coins aside for a later swap.MsgMoveIbcLockedCoinsfor the denom returns an error.Governance runbook (later, not in this PR)
MsgUpdateParamsreplaces the wholeParamsobject, so the proposal must carry every current field value plus the new list.bzed query tradebin params(mainnet REST:GET https://rest.getbze.com/bze/tradebin/params)./bze.tradebin.MsgUpdateParamsmessage:authority= the gov module account (mainnetbze10d07y265gmmuvt4z0w9aw880jnsr700j8xlwyy),params= the copied values plushalted_denoms: ["<denom>"]. Submit withbzed tx gov submit-proposal proposal.json.The runbook is also in
x/tradebin/docs/README.md.Changes
proto/bze/tradebin/v2/params.proto,x/tradebin/v2types/params.pb.go,api/bze/tradebin/v2/params.pulsar.go:repeated string halted_denoms = 13.x/tradebin/v2types/params.go:MaxHaltedDenoms = 32,Params.IsDenomHalted(exact match),Validatechecks every entry (sdk.ValidateDenom, not the native denom, no duplicates, at most 32).DefaultParams()leaves it empty.x/tradebin/types/errors.go:ErrDenomHalted(4017).x/tradebin/keeper/service_halt.go(new):IsDenomHalted,IsMarketHalted,isMarketHalted,isPoolHalted.x/tradebin/keeper/msg_server_amm.go:validateMarketAssets(covers create market + create pool),AddLiquidity,getRoutesPoolsrefuse halted denoms/pools;MultiSwapsurfacesErrDenomHaltedas-is instead of wrapping it inErrInvalidRoutes.x/tradebin/keeper/msg_server_order_book.go:CreateOrderandFillOrdersrefuse a halted market right afterGetMarketById.x/tradebin/keeper/service_amm.go:swapTokensrefuses a halted pool (the no-swap invariant).x/tradebin/keeper/service_denom.go: the three liquidity answers are false for a halted denom.x/tradebin/keeper/service_fee_payer.go: both fee-payer entry points fall back to capturing the native fee when the preferred denom's pool is halted.x/tradebin/docs/{params,README,technical}.md: param, effects, runbook, technical notes.scripts/testing/test-package.sh: the package pattern now also selectsv2types— the previous pattern (paths ending in/types) silently skippedx/tradebin/v2types, so its tests never ran in CI.CHANGELOG.md: entries in the open v8.2.0 section.Not changed:
queue_message_processor.goand the events proto. An earlier commit on this branch added an EndBlock refund path for queued messages on halted markets; it was removed in the last commit after review (see the commit message).Tests
x/tradebin/v2types/params_test.go: params table (empty valid, valid list, exactly 32 valid, native denom rejected, duplicate rejected, malformed/empty denom rejected, 33 rejected,DefaultParams()validates, exact-match semantics).x/tradebin/keeper/msg_server_halted_test.go: every listed message returnsErrDenomHaltedwith no bank expectation registered (gomock panics on an unexpected call, so a fee capture or escrow before the rejection would fail the test); halted pool as first and as second hop; unrelated market / pool unaffected;CancelOrderon a halted market queues normally;RemoveLiquidityon a halted pool pays out normally.x/tradebin/keeper/service_halt_test.go: helpers; the three liquidity answers false for a halted denom with a deep pool and unchanged otherwise;swapTokens(exposed viaexport_test.go) errors on a halted pool with untouched reserves and swaps again once un-halted;ModuleSwapForNativeDenomerrors and moves nothing;ModuleAddLiquidityWithNativeDenomrefunds the coin; both fee-payer entry points fall back to the native fee.x/tradebin/module/halted_denoms_wiring_test.go:MsgUpdateParamsthrough the real msg service router sets and clears the list from the gov authority, a non-authority signer and a list naming the native denom are rejected, genesis export/import round-trips and validates.Every behaviour test fails on the pre-change keeper code (production guards reverted, tests kept):
Verified locally:
go build ./...,gofmt,go vet ./x/tradebin/...,./scripts/testing/test-package.sh(all packages,-race).