dashboard: kolom TOTAL REWARD per pemilih + TXID jadi link explorer
This commit is contained in:
1 parent
344de409cd
commit
3ad5eb32d2
8 files changed
+112
-28
No files matched your search
@@ -26,10 +26,10 @@ Two tools scan the Vexanium blockchain voters table for accounts whose only vote
|
||||
- The voters table has a sentinel first row (`owner="...........q"`, uint64 0) that must be skipped.
|
||||
- Authoritative vote weight is `last_vote_weight` (high-precision decimal string) — stored verbatim as TEXT, never float-converted (spec §V6).
|
||||
- Freshness filter (spec §V13): `last_vote` (estimated last re-vote date) is derived at scan time from the Vexanium weight formula `last_vote_weight = staked_raw × 2^(years since 2000)` → `log2(weight / (staked×10000))`. Voters whose `last_vote` is older than `VEX_STALE_DAYS` (default 28) — or can't be derived (zero/empty weight) — are dropped in `normalize()` and never stored; the dashboard therefore shows only fresh voters (no stale UI). The heuristic over-reads when a voter unstaked without re-voting (weight ÷ smaller stake ⇒ future date), so `normalize()` clamps any `last_vote` past scan time down to `now`.
|
||||
- The web list shows RANK / AKUN / STAKE (VEX) / VOTE TERAKHIR columns (3 stats cells). Vote weight stays in the DB and `/api/search` JSON but is not rendered as a column.
|
||||
- The web list shows RANK / AKUN / STAKE (VEX) / TOTAL REWARD (VEX) / VOTE TERAKHIR columns (3 stats cells). TOTAL REWARD = all-time sum of `distribute_payments.amount` where `status='sent'` (0,0000 for never-paid), joined per page via a LEFT JOIN subquery. Vote weight stays in the DB and `/api/search` JSON but is not rendered as a column.
|
||||
- Token contract is `vex.token` (NOT `eosio.token` — that name doesn't exist on Vexanium), 4-decimal VEX, chain_id `f9f432b1851b5c179d2091a96f593aaed50ec7466b74f89301f957a83e56ce1f`. Distribution signs `vex.token::transfer` with the BP `active` key.
|
||||
- Distribution (spec §V19–V27): each run pays the whole liquid balance pro-rata by stored `staked`, shares floored at 4 decimals with dust left in the account, one transfer per voter. A txid that can't be confirmed (`GET {DATABISNIS_API}/v2/history/get_transaction?id=<txid>` returns no `executed`) is never re-sent the same run — that payment stays `failed` and rejoins the next day. No-op (exit 0, no writes) when the voters table is empty or balance < 0.0001. The dashboard's `/history` view renders `distribute_runs` + `distribute_payments` read-only.
|
||||
- Dashboard layout (spec §V28): desktop gives AKUN/STAKE/VOTE equal width with AKUN left and STAKE/VOTE centered; tablet keeps the fixed STAKE column near page center; `/history` uses a six-column run grid and a four-column payment grid (AKUN/JUMLAH/STATUS/TXID) that shows only the latest run with its date in the title — no TANGGAL or MEMO column (memo is on-chain only, not stored); mobile turns history rows into labeled cards. HTML responses use `Cache-Control: no-store`; stylesheet URL is versioned with `?v=` for deploy cache-busting.
|
||||
- Dashboard layout (spec §V28): desktop gives AKUN/STAKE/REWARD/VOTE equal width with AKUN left and the other three centered; tablet keeps the fixed STAKE column (RANK 56 / AKUN 1fr / STAKE 160px / REWARD 1fr / VOTE 1fr); `/history` uses a six-column run grid and a four-column payment grid (AKUN/JUMLAH/STATUS/TXID) that shows only the latest run with its date in the title, and links each TXID to `https://vexascan.com/transaction/{txid}` — no TANGGAL or MEMO column (memo is on-chain only, not stored); mobile turns history rows into labeled cards. HTML responses use `Cache-Control: no-store`; stylesheet URL is versioned with `?v=` for deploy cache-busting.
|
||||
- Liquid balance (spec §V16): a 4th stat cell "SALDO LIQUID" shows the BP account's liquid VEX (`account.core_liquid_balance`) fetched from `GET {DATABISNIS_API}/v2/state/get_account?account=<BP>`. The dashboard fetches it live but caches per-worker in memory for `DASH_LIQUID_TTL` (default 60s); a failed fetch keeps the last value (or renders `—` if none ever succeeded) and the failure is also cooled-down so the API isn't hammered. The dashboard still never writes to the DB.
|
||||
|
||||
## Layout
|
||||
@@ -39,7 +39,7 @@ Two tools scan the Vexanium blockchain voters table for accounts whose only vote
|
||||
- `db.py` — storage abstraction (sqlite default | mysql via PyMySQL); `connect/query/queryone/replace_snapshot`; `%s` → `?` for sqlite; `query` returns list. Juga tabel distribusi: `distribute_runs` + `distribute_payments` (append-only) + helper `ensure_distribute_schema/record_run/record_payment/update_payment_status/update_run_status/list_runs/list_payments`.
|
||||
- `get_voters.js` — reference implementation only.
|
||||
- `distribute.py` — pembayaran harian: fetch liquid balance → `compute_shares` (Decimal floor 4-des, sisa di akun) → sign `vex.token::transfer` via pyntelope (`trx.link` ambil ABI+TAPOS dari node, `sign` dengan `VEX_BP_PRIVATE_KEY`, `.send()`) → catat `sent/failed` per voter; verifikasi txid via Hyperion sebelum kirim ulang; `--dry-run` = rencana ⊥ tanda tangan. `test_distribute.py` — oracle (mock chain+pyntelope, temp DB).
|
||||
- `dashboard.py` + `templates/index.html` + `templates/history.html` + `static/style.css` + `static/app.js` — Flask web dashboard; reads the store via `db.py`, styled per `DESIGN.md`; `app.js` = debounced live owner search (fetch `/api/search`), degrades to the server-side `?q=` GET form if JS is off. `/history` = riwayat distribusi read-only; `run-list` = six-column desktop/tablet grid, `pay-list` = four-column grid (AKUN/JUMLAH/STATUS/TXID) showing only the latest run with its date in the title, and labeled mobile cards.
|
||||
- `dashboard.py` + `templates/index.html` + `templates/history.html` + `static/style.css` + `static/app.js` — Flask web dashboard; reads the store via `db.py`, styled per `DESIGN.md`; `app.js` = debounced live owner search (fetch `/api/search`), degrades to the server-side `?q=` GET form if JS is off. The voters list (`voter-list` section, `.reward` cells, V29) LEFT-joins a `status='sent'` payments aggregate for the TOTAL REWARD column. `/history` = riwayat distribusi read-only; `run-list` = six-column desktop/tablet grid, `pay-list` = four-column grid (AKUN/JUMLAH/STATUS/TXID) showing only the latest run with its date in the title, and labeled mobile cards.
|
||||
- `gunicorn.conf.py` — gunicorn production config (bind/workers from `config`, sync worker). `run.sh` — launcher: `./run.sh` = `./venv/bin/gunicorn -c gunicorn.conf.py dashboard:app` from any cwd.
|
||||
- `config.py` — loads env/`.env` (python-dotenv) → `TARGET_BP`, `API_NODE`, `DB_PATH`, `DB_BACKEND`, `DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASS`, `DB_NAME`, `MIN_STAKED_VEX`, `PAGE_SIZE`, `DASH_HOST`, `DASH_PORT`, `DASH_WORKERS`, `VEX_STALE_DAYS`, `DATABISNIS_API`, `DASH_LIQUID_TTL`, `BP_PRIVATE_KEY`, `DISTRIBUTE_HOUR`, `DISTRIBUTE_MAX_ATTEMPTS`; shared by get_voters & dashboard.
|
||||
- `scan_loop.py` — scheduler dalam container stack: menunggu batas `SCAN_HOURS` (lokal via `TZ`), panggil `get_voters.main()`; skan-awal `SCAN_RUN_ON_START` + tunggu DB siap; loop tak pernah keluar.
|
||||
|
||||
Reference in new issue
Block a user