feat(voting): operator-voting package — limited DB balances-at-height + signatures, proposals, blacklist #2
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
Create a new standalone package
operator-voting(sibling toindexer/ underpackages/as appropriate) that provides the offchain voting control plane with least-privilege Postgres access:Frontend and indexer remain separate; this package must not receive the indexer’s broad DB credentials.
CL8Y:
terra16wtml2q66g82fdkx66tap0qjkahqwp4lwq3ngtygacg5q0kzycgqvhpax3(fee-discount / hpax3). LocalTerra TCL8Y via shared env.Linked:
Current codebase
indexer/src/api/— almost entirely read DEX analytics; only notable POST isroute/solve(quote, not user auth). No signature verify, no vote routesDATABASE_URLwith full migrate/write — unsuitable to expose directly to a voting write APIindexer/Cargo.toml; frontend has Keplr tx sign (terraWalletSignTxRaw.ts) but nosignArbitrarytyping/helperspackages/localnet-trading-swarm(TS); Rust indexer is standalone cratecl8y-dex-indexer(not a workspace member with shared voting crate yet)indexer.dex.cl8y.com); pattern for a second service/env exists via deploy docsWhy this is needed
Voting must accept user-submitted signatures and persist them. Putting that write path on the full indexer DB role expands blast radius (analytics tables, ingestion state). A dedicated
operator-votingbinary/package with a restricted DB role (SELECT on balance views + CRUD on signature/proposal/vote tables only) isolates abuse and matches the product constraint: limited database access only for balances-at-height reads and signature read/write.Also centralizes ADR-36 verify, proposal rules (≥1000 CL8Y to create), snapshot freeze, tally, and
VOTING_BLACKLISTenforcement in one audit surface.Constraints / guardrails
operator-votingDB user must not INSERT/UPDATE indexer ledger tables,indexer_state, swap tables, etc. Prefer dedicated schema (e.g.voting) +GRANT SELECTon balance view/function from indexer migrations.signArbitrary) → recover/deriveterra1…and require it matches claimed voter/proposer/registrar.Husing Postgres balance-at-height only (no archive LCD).VOTING_BLACKLIST_ADDRESSESorOPERATOR_VOTING_BLACKLIST) — blacklisted wallets cannot vote (and document whether they can still register/create proposals; default: cannot vote; creating proposals as blacklisted should also be denied unless product says otherwise — deny both vote and propose).(proposal_id, wallet); proposal create is not replayable with same id.operator-votingas requested; shipable as its own Coolify service with its ownDATABASE_URL(restricted).Relevant files
New
operator-voting/orpackages/operator-voting/— crate/package root (Cargo.tomlor Node — prefer Rust to share sqlx/postgres ops with indexer, unless team standardizes TS; recommend Rust Axum sibling binary)operator-votingor additive indexer migrations that create views +votingschema with grantsReuse / integrate
indexer/src/api/mod.rspatterns (utoipa, CORS, governor rate limits) as reference — do not merge routes into indexerVITE_OPERATOR_VOTING_URL(frontend issue)Reference only
frontend-dapp/src/services/terraclassic/terraWalletSignTxRaw.ts,wallet.tsdocs/security-model.md, Coolify deploy runbooksRecommended direction
Responsibilities
POST /v1/registerGET /v1/registration/:addrPOST /v1/proposalsGET /v1/proposals,GET /v1/proposals/:idPOST /v1/proposals/:id/votesGET /v1/proposals/:id/votes/:addrGET /v1/balances/:addr?height=Env
DATABASE_URL(restricted role)CL8Y_TOKEN_ADDRESSVOTING_BLACKLIST_ADDRESSES— comma-separatedterra1…(trim whitespace; invalid entries fail startup)CORS_ORIGINS,API_BIND, chain-id / signing domain constantsMIN_PROPOSAL_CL8Ydefault1000(human units) →1000 * 10^18rawDB
signatures,proposals,proposal_snapshots(wallet → weight),votesvoting_cl8y_balance_at(address, height)from indexer migrationsCrypto
signArbitraryoncolumbus-5/ LocalTerra chain-idAcceptance criteria
operator-votingpackage builds and runs as its own service.VOTING_BLACKLIST_ADDRESSES(or final agreed name) parsed from env; blacklisted addresses cannot vote (and cannot create proposals per guardrail above).Test plan (all paths)
1000 * 10^18).swap_events/ ledger tables fails.Test plan — attack, hack & abuse vectors
voterfield ≠ recovered address — reject.Verification criteria
operator-voting.Labels
backendindexerenhancementarchitecturesecuritydeployPriority
P1
mentioned in issue cl8y-dex-terraclassic#509
Bundle links
operator-voting— signatures, proposals, votes,VOTING_BLACKLIST_ADDRESSESDepends on cl8y-dex-terraclassic#509 balance views/grants. Blocks cl8y-dex-terraclassic#511 for full-stack E2E.
mentioned in issue cl8y-dex-terraclassic#511
marked as related to cl8y-dex-terraclassic#509
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: EVM signatures + BSC snapshot weight (core)
A majority of CL8Y holders are on BSC, not Terra Classic.
operator-votingmust verify EIP-191personal_signfrom EVM wallets (in addition to Terra ADR-36), persist signatures for0xaddresses, and freeze vote power from the BSC BEP-20 ledger as well as the Terra CW20 ledger.Proposal create should record both a Terra Classic height and a BSC block number.
VOTING_BLACKLIST_ADDRESSESmust accept normalized EVM addresses.Terra-only auth/weights would exclude most tokenholders — not an acceptable MVP.
Tracking issue (BSC integration): https://gitlab.com/PlasticDigits/cl8y-dex-terraclassic/-/issues/588
moved from cl8y-dex-terraclassic#510
mentioned in issue #6
mentioned in issue #5
mentioned in issue #1
changed the description
mentioned in issue #3
mentioned in issue #4
Repo split + updated links (2026-08-25)
Moved from
cl8y-dex-terraclassic#510to https://gitlab.com/PlasticDigits/voting/-/issues/2operator-votingis a first-class package in this repo (not under the DEX indexer).mentioned in issue cl8y-dex-terraclassic#510
mentioned in issue cl8y-dex-terraclassic#637
mentioned 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).
operator-votingis on main: ADR-36 + EIP-191, dual snapshot, blacklist, restricted-role test.Still open after merge (tracked in #7):
DATABASE_URL(must not be the ledger writer).mentioned in issue #9
mentioned in issue #13
mentioned in issue #14