Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [6.8.0] - 2026-09-02

### Added

- New `counted` field in `/governance/proposals/:tx_hash/:cert_index/votes`, indicating whether the vote counts toward the proposal's tally: only the voter's latest vote counts, and a DRep vote stops counting if the DRep deregisters while the proposal is still live (blockfrost/openapi#466)

### Changed

- Constitutional committee voters in `/governance/proposals/:tx_hash/:cert_index/votes` are now CIP-129 `cc_hot1...` ids instead of raw hex (blockfrost/openapi#465)
- Updated `@blockfrost/openapi` to 0.1.93

## [6.7.2] - 2026-08-05

### Added
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "blockfrost-backend-ryo",
"version": "6.7.2",
"version": "6.8.0",
"description": "",
"keywords": [],
"license": "Apache-2.0",
Expand All @@ -23,7 +23,7 @@
"dependencies": {
"@blockfrost/blockfrost-js": "6.1.1",
"@blockfrost/blockfrost-utils": "3.0.0",
"@blockfrost/openapi": "0.1.91",
"@blockfrost/openapi": "0.1.93",
"@fastify/cors": "^11.1.0",
"@fastify/http-proxy": "^11.5.0",
"@fastify/postgres": "^6.0.2",
Expand Down
81 changes: 66 additions & 15 deletions src/sql/governance/proposals_proposal_votes.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,41 @@
WITH proposal_votes AS (
SELECT vp.id,
vp.tx_id,
vp.index,
vp.voter_role,
vp.drep_voter,
vp.pool_voter,
vp.committee_voter,
vp.vote,
-- Epoch in which the proposal left the active set; its vote tally is frozen
-- from that point on
LEAST(
gap.ratified_epoch,
gap.enacted_epoch,
gap.dropped_epoch,
gap.expired_epoch
) AS closed_epoch,
-- The ledger keeps only the latest vote per voter and proposal (upsert semantics)
ROW_NUMBER() OVER (
PARTITION BY vp.voter_role,
vp.drep_voter,
vp.pool_voter,
vp.committee_voter
ORDER BY vp.tx_id DESC,
vp.id DESC
) AS recency_rank
FROM gov_action_proposal gap
JOIN tx gap_tx ON (gap_tx.id = gap.tx_id)
JOIN voting_procedure vp ON (vp.gov_action_proposal_id = gap.id)
WHERE encode(gap_tx.hash, 'hex') = $4
AND gap.index = $5
)
SELECT encode(vp_tx.hash, 'hex') AS "tx_hash",
vp.index AS "cert_index",
p.index AS "cert_index",
(
CASE
WHEN vp.voter_role::TEXT = 'ConstitutionalCommittee' THEN 'constitutional_committee'
ELSE LOWER(vp.voter_role::TEXT)
WHEN p.voter_role::TEXT = 'ConstitutionalCommittee' THEN 'constitutional_committee'
ELSE LOWER(p.voter_role::TEXT)
END
) AS "voter_role",
-- ConstitutionalCommittee, DRep, SPO -> constitutional_committee, drep, spo
Expand All @@ -12,22 +44,41 @@ SELECT encode(vp_tx.hash, 'hex') AS "tx_hash",
) AS "voter",
dh.has_script AS "voter_has_script",
ch.has_script AS "cc_voter_has_script",
LOWER(vote::TEXT) AS "vote" -- Yes, No, Abstain -> yes,no,abstain
FROM gov_action_proposal gap
JOIN tx gap_tx ON (gap_tx.id = gap.tx_id)
JOIN voting_procedure vp ON (vp.gov_action_proposal_id = gap.id)
JOIN tx vp_tx ON (vp_tx.id = vp.tx_id)
LEFT JOIN drep_hash dh ON (vp.drep_voter = dh.id)
LEFT JOIN pool_hash ph ON (vp.pool_voter = ph.id)
LEFT JOIN committee_hash ch ON (vp.committee_voter = ch.id)
WHERE encode(gap_tx.hash, 'hex') = $4
AND gap.index = $5
LOWER(p.vote::TEXT) AS "vote",
-- Yes, No, Abstain -> yes,no,abstain
(
p.recency_rank = 1
AND (
p.drep_voter IS NULL
-- deregistration drops the DRep's votes from the ledger and re-registering
-- does not restore them, but only while the proposal is still live:
-- once it closes the tally is frozen and later deregistrations have no effect
OR NOT EXISTS (
SELECT 1
FROM drep_registration dr
JOIN tx dr_tx ON (dr_tx.id = dr.tx_id)
JOIN block dr_b ON (dr_b.id = dr_tx.block_id)
WHERE dr.drep_hash_id = p.drep_voter
AND dr.deposit < 0
AND dr.tx_id > p.tx_id
AND (
p.closed_epoch IS NULL
OR dr_b.epoch_no < p.closed_epoch
)
)
)
) AS "counted"
FROM proposal_votes p
JOIN tx vp_tx ON (vp_tx.id = p.tx_id)
LEFT JOIN drep_hash dh ON (p.drep_voter = dh.id)
LEFT JOIN pool_hash ph ON (p.pool_voter = ph.id)
LEFT JOIN committee_hash ch ON (p.committee_voter = ch.id)
ORDER BY CASE
WHEN LOWER($1) = 'desc' THEN vp.id
WHEN LOWER($1) = 'desc' THEN p.id
END DESC,
CASE
WHEN LOWER($1) <> 'desc'
OR $1 IS NULL THEN vp.id
OR $1 IS NULL THEN p.id
END ASC
LIMIT CASE
WHEN $2 >= 1
Expand Down
81 changes: 66 additions & 15 deletions src/sql/governance/unpaged/proposals_proposal_votes.sql
Original file line number Diff line number Diff line change
@@ -1,9 +1,41 @@
WITH proposal_votes AS (
SELECT vp.id,
vp.tx_id,
vp.index,
vp.voter_role,
vp.drep_voter,
vp.pool_voter,
vp.committee_voter,
vp.vote,
-- Epoch in which the proposal left the active set; its vote tally is frozen
-- from that point on
LEAST(
gap.ratified_epoch,
gap.enacted_epoch,
gap.dropped_epoch,
gap.expired_epoch
) AS closed_epoch,
-- The ledger keeps only the latest vote per voter and proposal (upsert semantics)
ROW_NUMBER() OVER (
PARTITION BY vp.voter_role,
vp.drep_voter,
vp.pool_voter,
vp.committee_voter
ORDER BY vp.tx_id DESC,
vp.id DESC
) AS recency_rank
FROM gov_action_proposal gap
JOIN tx gap_tx ON (gap_tx.id = gap.tx_id)
JOIN voting_procedure vp ON (vp.gov_action_proposal_id = gap.id)
WHERE encode(gap_tx.hash, 'hex') = $2
AND gap.index = $3
)
SELECT encode(vp_tx.hash, 'hex') AS "tx_hash",
vp.index AS "cert_index",
p.index AS "cert_index",
(
CASE
WHEN vp.voter_role::TEXT = 'ConstitutionalCommittee' THEN 'constitutional_committee'
ELSE LOWER(vp.voter_role::TEXT)
WHEN p.voter_role::TEXT = 'ConstitutionalCommittee' THEN 'constitutional_committee'
ELSE LOWER(p.voter_role::TEXT)
END
) AS "voter_role",
-- ConstitutionalCommittee, DRep, SPO -> constitutional_committee, drep, spo
Expand All @@ -12,20 +44,39 @@ SELECT encode(vp_tx.hash, 'hex') AS "tx_hash",
) AS "voter",
dh.has_script AS "voter_has_script",
ch.has_script AS "cc_voter_has_script",
LOWER(vote::TEXT) AS "vote" -- Yes, No, Abstain -> yes,no,abstain
FROM gov_action_proposal gap
JOIN tx gap_tx ON (gap_tx.id = gap.tx_id)
JOIN voting_procedure vp ON (vp.gov_action_proposal_id = gap.id)
JOIN tx vp_tx ON (vp_tx.id = vp.tx_id)
LEFT JOIN drep_hash dh ON (vp.drep_voter = dh.id)
LEFT JOIN pool_hash ph ON (vp.pool_voter = ph.id)
LEFT JOIN committee_hash ch ON (vp.committee_voter = ch.id)
WHERE encode(gap_tx.hash, 'hex') = $2
AND gap.index = $3
LOWER(p.vote::TEXT) AS "vote",
-- Yes, No, Abstain -> yes,no,abstain
(
p.recency_rank = 1
AND (
p.drep_voter IS NULL
-- deregistration drops the DRep's votes from the ledger and re-registering
-- does not restore them, but only while the proposal is still live:
-- once it closes the tally is frozen and later deregistrations have no effect
OR NOT EXISTS (
SELECT 1
FROM drep_registration dr
JOIN tx dr_tx ON (dr_tx.id = dr.tx_id)
JOIN block dr_b ON (dr_b.id = dr_tx.block_id)
WHERE dr.drep_hash_id = p.drep_voter
AND dr.deposit < 0
AND dr.tx_id > p.tx_id
AND (
p.closed_epoch IS NULL
OR dr_b.epoch_no < p.closed_epoch
)
)
)
) AS "counted"
FROM proposal_votes p
JOIN tx vp_tx ON (vp_tx.id = p.tx_id)
LEFT JOIN drep_hash dh ON (p.drep_voter = dh.id)
LEFT JOIN pool_hash ph ON (p.pool_voter = ph.id)
LEFT JOIN committee_hash ch ON (p.committee_voter = ch.id)
ORDER BY CASE
WHEN LOWER($1) = 'desc' THEN vp.id
WHEN LOWER($1) = 'desc' THEN p.id
END DESC,
CASE
WHEN LOWER($1) <> 'desc'
OR $1 IS NULL THEN vp.id
OR $1 IS NULL THEN p.id
END ASC
1 change: 1 addition & 0 deletions src/types/queries/governance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export interface ProposalsProposalVotes {
voter_has_script: boolean;
cc_voter_has_script: boolean | null;
vote: 'yes' | 'no' | 'abstain';
counted: boolean;
}

export interface ProposalsProposalMetadata {
Expand Down
10 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -101,15 +101,15 @@ __metadata:
languageName: node
linkType: hard

"@blockfrost/openapi@npm:0.1.91":
version: 0.1.91
resolution: "@blockfrost/openapi@npm:0.1.91"
"@blockfrost/openapi@npm:0.1.93":
version: 0.1.93
resolution: "@blockfrost/openapi@npm:0.1.93"
dependencies:
ajv: ^8.17.1
cbor: ^9.0.2
rimraf: 6.0.1
yaml: ^2.6.1
checksum: d33b439dcd11519d4a0578343d7de5909eeb35b86929f1bd8ac9b5e73534f42508cc363a8b68e9fec4ac804849fc8c393e42c64c3015873716cefcebbf4090d5
checksum: 0273c7f948962fe1739a3dae2c26e06fac81443af799afcc72350441f5b165daf2b4b43ede76e092ee180dc558be676fc95dba44e427fd1f0f59158f92504bb5
languageName: node
linkType: hard

Expand Down Expand Up @@ -1956,7 +1956,7 @@ __metadata:
dependencies:
"@blockfrost/blockfrost-js": 6.1.1
"@blockfrost/blockfrost-utils": 3.0.0
"@blockfrost/openapi": 0.1.91
"@blockfrost/openapi": 0.1.93
"@eslint/compat": ^2.0.0
"@eslint/eslintrc": ^3.3.3
"@eslint/js": ^9.39.1
Expand Down
Loading