feat(voting): indexer registration + CL8Y CW20 balance ledger for registered wallets (no archive LCD) #1
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Add an offchain CL8Y (hpax3) voter registration + balance ledger in the indexer/Postgres stack so snapshot vote power can be computed without an archive LCD.
CL8Y token (same as fee-discount
cl8y_token):terra16wtml2q66g82fdkx66tap0qjkahqwp4lwq3ngtygacg5q0kzycgqvhpax3(18 decimals). LocalTerra uses TCL8Y viaVITE_CL8Y_TOKEN_ADDRESS.Product flow (this issue owns the data plane):
operator-voting— linked issue).Balance+ current block height (no historical/archive query).transfer/send/ mint-burn (if any) events to/from registered wallets, maintaining a running balance and height-addressable history for those wallets.Depends on / pairs with:
operator-votingpackage (signatures + restricted balance reads)Current codebase
frontend-dapp/src/utils/constants.ts(VITE_CL8Y_TOKEN_ADDRESS/ hpax3 fallback),scripts/lib/mainnet-soft-launch-defaults.sh(MAINNET_CL8Y_TOKEN_ADDRESS), fee-discountcl8y_tokengetTokenBalanceinfrontend-dapp/src/services/terraclassic/queries.ts; fee-discount contract also queriesCw20QueryMsg::Balanceon register /GetDiscountindexer/src/lcd/mod.rsquery_contract; usestoken_info/ fee-discount registration — no CW20Balancehelper, nox-cosmos-block-heightarchive queriesget_latest_block_height/get_block_at_heightfor indexing/reorg onlyindexer/src/indexer/parser.rs— DEX swaps, limits, hooks, fee-discount registry; does not index CW20 Transfer/Send for arbitrary tokensindexer/migrations/—assets,traders,trader_positions,pair_reserves, etc. No holder/balance/transfer tables.trader_positionsare explicitly not on-chain balances (docs/indexer-invariants.md)operator-votingWhy this is needed
Community / operator snapshot voting needs height-frozen CL8Y vote power without operating an archive node. Enumerating all CL8Y holders on mainnet LCD is slow/unreliable. Opt-in registration + transfer tracking for the registered set gives a closed electorate with balances reconstructible at proposal heights from Postgres alone after registration.
Same CL8Y that gates fee-discount tiers should gate voting weight for consistency.
Constraints / guardrails
operator-voting) only.x-cosmos-block-height.CL8Y_TOKEN_ADDRESS/ reuse fee-discount config /VITE_CL8Y_TOKEN_ADDRESSparity on LocalTerra TCL8Y).NUMERICwithout float drift._contract_address(indexer invariant cl8y-dex-terraclassic#285) — only trust CL8Y contract emissions.transfer,send, and any mint/burn actions the deployed code ID emits; ignore unrelated wasm.indexer_state.last_indexed_heightpatterns; balances must be correct after reorg unwind/replay for registered wallets.traders.tier_idortrader_positionsas vote weight.operator-votingmust only read balances (restricted role/views) — see linked package issue.Relevant files
Indexer / DB
indexer/migrations/(new migration(s))indexer/src/indexer/parser.rs,block_indexer.rs,poller.rs,mod.rsindexer/src/lcd/mod.rs,lcd/types.rsindexer/src/db/queries/,db/mod.rsindexer/src/config.rs,startup.rs,main.rsindexer/src/indexer/trader_tracker.rs(pattern for fee-discount events — do not overload)docs/indexer-invariants.mdCL8Y / balance references
frontend-dapp/src/utils/constants.ts,tokenRegistry.tsfrontend-dapp/src/services/terraclassic/queries.tsscripts/lib/mainnet-soft-launch-defaults.shdocs/reference/fee-discount-tiers.mdsmartcontracts/contracts/fee-discount/(balance query semantics reference only — no contract changes)Deploy
docker/indexer/Dockerfile, Coolify indexer env examples underdeployments/Recommended direction
Schema (illustrative)
voting_registrations—wallet_address,registered_at_height,registered_at_time,initial_balance,status, optional link to signature id owned byoperator-votingcl8y_cw20_transfers— height, tx hash, from, to, amount, action, only rows involving ≥1 registered wallet + CL8Y contractcl8y_balancesor height-addressable checkpoints — running balance per registered wallet after each affecting transfer; enough to answerbalance(wallet, height)forheight >= registered_at_height(0 or null before registration)Registration ingest
operator-votingverifies ADR-36 (or chosen) signature and inserts/requests registration.{"balance":{"address":...}}on CL8Y CW20initial_balance+ heightregistered_at_heightforward, transfer parser updates that wallet’s ledger.Transfer indexing
parser.rs(or dedicated module): when_contract_address == CL8Y_TOKENand action ∈ {transfer, send, …}, iffromortois registered, append transfer + update balances.Balance-at-height API surface for
operator-votingvoting_cl8y_balance_at(wallet, height)granted only to theoperator_votingDB role — no broad table write grants.LocalTerra
Acceptance criteria
(wallet, height).balance(wallet, H)forH >= registered_at_heightmatches LCD live balance after replaying transfers up to H (property/integration tested on LocalTerra).Test plan (all paths)
_contract_address; registered vs unregistered from/to; forgedcontract_addresswithout underscore ignored (cl8y-dex-terraclassic#285).Test plan — attack, hack & abuse vectors
contract_address(no underscore) claiming CL8Y transfer — must not credit.Verification criteria
cargo testfor indexer units covering parser + balance helpers.balance_at(LocalTerra or fixtures).x-cosmos-block-heightin the registration/ledger path.operator-votingissue can read balances via restricted DB role only.Labels
indexerbackendenhancementarchitecturesecurityPriority
P1
Bundle links
Offchain CL8Y snapshot voting (no contracts, no archive LCD):
operator-votingpackage — restricted DB, signatures, proposals, votes, blacklist envSuggested order: cl8y-dex-terraclassic#509 → cl8y-dex-terraclassic#510 → cl8y-dex-terraclassic#511 (UI can stub cl8y-dex-terraclassic#510 with MSW in parallel).
mentioned in issue cl8y-dex-terraclassic#510
mentioned in issue cl8y-dex-terraclassic#511
marked as related to cl8y-dex-terraclassic#510
marked as related to cl8y-dex-terraclassic#511
mentioned in issue cl8y-dex-terraclassic#588
marked as related to cl8y-dex-terraclassic#588
Scope add: BSC BEP-20 CL8Y ledger (core)
A majority of CL8Y holders are on BSC, not Terra Classic. The registration + balance ledger in this issue must also track BEP-20 CL8Y for registered EVM wallets — same “live balance at registration + transfers forward, no archive node” model as CW20.
Canonical BEP-20:
0x8F452a1fdd388A45e1080992eFF051b4dd9048d2Do not treat Terra CW20-only as a complete data plane. Reuse
BSC_RPC_URLStransport; do not overload the Venus vFDUSD poller (cl8y-dex-terraclassic#571).Tracking issue (BSC integration): https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/588
moved from cl8y-dex-terraclassic#509
mentioned in issue #6
mentioned in issue #5
Repo split + updated links (2026-08-25)
This issue moved from
cl8y-dex-terraclassic#509to https://gitlab.com/PlasticDigits/voting/-/issues/1Implement the ledger in this repo. DEX
indexer/paths in the description are references only./voteUXchanged the description
mentioned in issue #2
mentioned in issue #3
mentioned in issue #4
mentioned in issue cl8y-dex-terraclassic#509
mentioned in issue cl8y-dex-terraclassic#637
mentioned in commit
8d11152cfementioned in commit
b32b56660dmentioned in merge request !18
Implemented on feat/voting-bundle. MR: https://gitlab.com/PlasticDigits/voting/-/merge_requests/1
In-tree: ledger/ (CW20 + BEP-20), operator-voting/, frontend/ (/vote + Legal + ported wallets).
Docs: docs/LEDGER_INVARIANTS.md, docs/OPERATOR_VOTING.md, docs/FRONTEND.md.
mentioned in commit
8fe45a1974mentioned in issue #7
Merge verify (!18 → main, no conflicts, fast-forward). In-tree ledger criteria are met on
8fe45a1.Still open after merge (tracked in #7):
mentioned in issue #9
mentioned in issue #14