diff --git a/.github/scripts/set_commit_status.sh b/.github/scripts/set_commit_status.sh new file mode 100644 index 000000000..01a822304 --- /dev/null +++ b/.github/scripts/set_commit_status.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# Ensure required environment variables are set +if [ -z "$GITHUB_REPOSITORY" ] || [ -z "$GITHUB_SHA" ] || [ -z "$GITHUB_TOKEN" ] || [ -z "$GITHUB_RUN_ID" ]; then + echo "Missing required environment variables!" + exit 1 +fi + +# Retrieve necessary variables from workflow +START_TIME=${START_TIME:-$(date +%s)} +TEST_STATUS=${TEST_STATUS:-"failure"} +REPORT_NUMBER=${REPORT_NUMBER:-1} +REPORT_NAME=${REPORT_NAME:-"govtool-frontend"} +HOST_URL=${HOST_URL:-"https://govtool.cardanoapi.io"} +CONTEXT="Playwright Tests : $HOST_URL" + +if [[ "$REPORT_NAME" == "govtool-backend" ]]; then + CONTEXT="Backend Tests : $BASE_URL" +fi + +# Parse Allure JSON results +if [ -z "$(ls -A allure-results/*.json 2>/dev/null)" ]; then + echo "No Allure JSON results found!" + PASSED=0 + FAILED=0 + TOTAL=0 +else + PASSED=$(jq -s '[.[] | select(.status == "passed")] | length' allure-results/*.json) + FAILED=$(jq -s '[.[] | select(.status == "failed")] | length' allure-results/*.json) + TOTAL=$((PASSED + FAILED)) +fi + + + +# Calculate test duration +CURRENT_TIME=$(date +%s) +TEST_DURATION_SECONDS=$((CURRENT_TIME - START_TIME)) +TEST_DURATION_MINUTES=$((TEST_DURATION_SECONDS / 60)) +TEST_DURATION_HOURS=$((TEST_DURATION_MINUTES / 60)) + +if [ "$TEST_DURATION_HOURS" -gt 0 ]; then + TEST_DURATION="${TEST_DURATION_HOURS} hour$( [ "$TEST_DURATION_HOURS" -ne 1 ] && echo "s"), $((TEST_DURATION_MINUTES % 60)) minute$( [ "$((TEST_DURATION_MINUTES % 60))" -ne 1 ] && echo "s"), and $((TEST_DURATION_SECONDS % 60)) second$( [ "$((TEST_DURATION_SECONDS % 60))" -ne 1 ] && echo "s")" +elif [ "$TEST_DURATION_MINUTES" -gt 0 ]; then + TEST_DURATION="${TEST_DURATION_MINUTES} minute$( [ "$TEST_DURATION_MINUTES" -ne 1 ] && echo "s") and $((TEST_DURATION_SECONDS % 60)) second$( [ "$((TEST_DURATION_SECONDS % 60))" -ne 1 ] && echo "s")" +else + TEST_DURATION="${TEST_DURATION_SECONDS} second$( [ "$TEST_DURATION_SECONDS" -ne 1 ] && echo "s")" +fi + +# Determine target URL based on environment +case "$GH_PAGES" in + "IntersectMBO/govtool-test-reports") TARGET_URL="https://intersectmbo.github.io/govtool-test-reports/${REPORT_NAME}/${REPORT_NUMBER}" ;; + "cardanoapi/govtool-test-reports") TARGET_URL="https://cardanoapi.github.io/govtool-test-reports/${REPORT_NAME}/${REPORT_NUMBER}" ;; + *) TARGET_URL="https://intersectmbo.github.io/govtool-test-reports/${REPORT_NAME}/${REPORT_NUMBER}" ;; +esac + +# Determine test result message +if [[ "$TEST_STATUS" == "success" ]]; then + DESCRIPTION="Tests passed in ${TEST_DURATION}" +elif [[ "$TEST_STATUS" == "failure" && "$TOTAL" -ne 0 ]]; then + DESCRIPTION="Tests failed in ${TEST_DURATION}: Passed ${PASSED}, Failed ${FAILED} out of ${TOTAL}" +else + DESCRIPTION="⚠️ Tests execution failed :$TEST_STATUS" + TEST_STATUS="error" + TARGET_URL="https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" +fi + + + +# Send commit status update to GitHub +curl -X POST -H "Authorization: Bearer ${GITHUB_TOKEN}" \ + -H "Accept: application/vnd.github+json" \ + https://api.github.com/repos/${GITHUB_REPOSITORY}/statuses/${GITHUB_SHA} \ + -d "{\"state\": \"${TEST_STATUS}\", \"context\": \"${CONTEXT}\", \"description\": \"${DESCRIPTION}\", \"target_url\": \"${TARGET_URL}\"}" + +echo "Commit status updated successfully!" diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml index 22ccffaa0..59488d061 100644 --- a/.github/workflows/test_backend.yml +++ b/.github/workflows/test_backend.yml @@ -1,5 +1,10 @@ name: Backend Test +permissions: + contents: read + checks: write + statuses: write + on: workflow_dispatch: inputs: @@ -27,14 +32,31 @@ on: workflows: ["Build and deploy GovTool test stack"] types: [completed] +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false + jobs: backend-tests: runs-on: ubuntu-latest if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + outputs: + start_time: ${{ steps.set-pending-status.outputs.timestamp }} + status: ${{ steps.run-tests.outcome }} steps: - name: Checkout code uses: actions/checkout@v4 + - name: Set pending commit status + id: set-pending-status + run: | + echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT + curl -X POST -H "Authorization: Bearer ${{ github.token }}" \ + -H "Accept: application/vnd.github+json" \ + https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} \ + -d "{\"state\": \"pending\", \"context\": \"Backend Tests : ${{env.BASE_URL}}\", \"target_url\": \"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" + + - name: Set up Python uses: actions/setup-python@v4 with: @@ -43,6 +65,7 @@ jobs: - name: Run Backend Test working-directory: tests/govtool-backend + id: run-tests run: | python -m pip install --upgrade pip pip install -r requirements.txt @@ -55,26 +78,27 @@ jobs: fi python ./setup.py python -m pytest --alluredir allure-results - env: - BASE_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io/api' }} - NETWORK: ${{ inputs.network || vars.NETWORK }} - KUBER_API_KEY: ${{ secrets.KUBER_API_KEY }} - name: Upload report if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: allure-results path: tests/govtool-backend/allure-results + env: + NETWORK: ${{ inputs.network || vars.NETWORK }} + KUBER_API_KEY: ${{ secrets.KUBER_API_KEY }} publish-report: runs-on: ubuntu-latest if: always() && needs.backend-tests.result != 'skipped' needs: backend-tests + outputs: + report_number: ${{ steps.report-details.outputs.report_number }} steps: - uses: actions/checkout@v4 - name: Download results - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: allure-results path: allure-results @@ -134,6 +158,29 @@ jobs: folder: build target-folder: ${{ env.REPORT_NAME }} - env: - REPORT_NAME: govtool-backend - GH_PAGES: ${{vars.GH_PAGES}} + publish-status: + runs-on: ubuntu-latest + if: always() + needs: [backend-tests, publish-report] + steps: + - uses: actions/checkout@v4 + - name: Download results + uses: actions/download-artifact@v4 + with: + name: allure-results + path: allure-results + - name: Set Commit Status + if: always() + run: | + chmod +x .github/scripts/set_commit_status.sh + .github/scripts/set_commit_status.sh + env: + START_TIME: ${{ needs.backend-tests.outputs.start_time }} + TEST_STATUS: ${{ needs.backend-tests.outputs.status }} + REPORT_NUMBER: ${{ needs.publish-report.outputs.report_number }} + GITHUB_TOKEN: ${{ github.token }} + +env: + BASE_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io/api' }} + REPORT_NAME: govtool-backend + GH_PAGES: ${{vars.GH_PAGES}} diff --git a/.github/workflows/test_integration_playwright.yml b/.github/workflows/test_integration_playwright.yml index b00cee48f..b3c1591db 100644 --- a/.github/workflows/test_integration_playwright.yml +++ b/.github/workflows/test_integration_playwright.yml @@ -1,5 +1,10 @@ name: Integration Test [Playwright] +permissions: + contents: read + checks: write + statuses: write + on: workflow_dispatch: inputs: @@ -26,16 +31,32 @@ on: workflow_run: workflows: ["Build and deploy GovTool test stack"] types: [completed] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: false jobs: integration-tests: runs-on: ubuntu-latest if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} + outputs: + start_time: ${{ steps.set-pending-status.outputs.timestamp }} + status: ${{ steps.run-test.outcome }} defaults: run: working-directory: tests/govtool-frontend/playwright steps: - uses: actions/checkout@v4 + - name: Set pending commit status + id: set-pending-status + run: | + echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT + curl -X POST -H "Authorization: Bearer ${{ github.token }}" \ + -H "Accept: application/vnd.github+json" \ + https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} \ + -d "{\"state\": \"pending\", \"context\": \"Playwright Tests : ${{env.HOST_URL}}\", \"target_url\": \"https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" + - uses: actions/setup-node@v4 with: node-version: "18" @@ -58,6 +79,7 @@ jobs: run: npx playwright install --with-deps - name: Run tests + id: run-test run: | mkdir -p ./lib/_mock chmod +w ./lib/_mock @@ -81,21 +103,20 @@ jobs: npm test - name: Upload report - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: allure-results path: tests/govtool-frontend/playwright/allure-results - name: Upload lock logs - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: always() with: name: lock-logs path: tests/govtool-frontend/playwright/lock_logs.txt env: - HOST_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io' }} API_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io' }}/api DOCS_URL: ${{ vars.DOCS_URL }} KUBER_API_KEY: ${{secrets.KUBER_API_KEY}} @@ -111,10 +132,12 @@ jobs: runs-on: ubuntu-latest if: always() && needs.integration-tests.result != 'skipped' needs: integration-tests + outputs: + report_number: ${{ steps.report-details.outputs.report_number }} steps: - uses: actions/checkout@v4 - name: Download report - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: allure-results path: allure-results @@ -175,6 +198,28 @@ jobs: folder: build target-folder: ${{ env.REPORT_NAME }} - env: - REPORT_NAME: govtool-frontend - GH_PAGES: ${{vars.GH_PAGES}} + publish-status: + runs-on: ubuntu-latest + if: always() + needs: [integration-tests, publish-report] + steps: + - uses: actions/checkout@v4 + - name: Download results + uses: actions/download-artifact@v4 + with: + name: allure-results + path: allure-results + - name: Set Commit Status + if: always() + run: | + chmod +x .github/scripts/set_commit_status.sh + .github/scripts/set_commit_status.sh + env: + START_TIME: ${{ needs.integration-tests.outputs.start_time }} + TEST_STATUS: ${{ needs.integration-tests.outputs.status }} + REPORT_NUMBER: ${{ needs.publish-report.outputs.report_number }} + GITHUB_TOKEN: ${{ github.token }} +env: + HOST_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io' }} + REPORT_NAME: govtool-frontend + GH_PAGES: ${{vars.GH_PAGES}} diff --git a/CHANGELOG.md b/CHANGELOG.md index 36fae25f9..868a18f7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,19 +12,22 @@ changes. ### Added -- +- Add metadata url and hash to drep details [Issue 2911](https://github.com/IntersectMBO/govtool/issues/2911) ### Fixed -- +- Fix calculating votes counting for governance actions +- Fix crashing backend on unhandled missing proposal from vote [Issue 2920](https://github.com/IntersectMBO/govtool/issues/2920) +- Remove abstain votes (not auto abstain) from total DRep stake ### Changed - Change threshold visual representation in governance action votes +- Resize governance action details columns ### Removed -- +- Remove abstain from total DRep votes calculation ## [v2.0.11](https://github.com/IntersectMBO/govtool/releases/tag/v2.0.11) 2025-02-04 diff --git a/govtool/backend/sql/get-network-metrics.sql b/govtool/backend/sql/get-network-metrics.sql index 17404faa7..ba668900f 100644 --- a/govtool/backend/sql/get-network-metrics.sql +++ b/govtool/backend/sql/get-network-metrics.sql @@ -10,222 +10,215 @@ WITH DRepActivity AS ( epoch_no DESC LIMIT 1 ), -active_drep_boundary_epoch AS ( - SELECT - epoch_no - drep_activity AS epoch_no - FROM - DRepActivity -), -RankedDRep AS ( +DRepDistr AS ( SELECT - dh.raw AS drep_hash_raw, - b.epoch_no, - dr.deposit, - dr.voting_anchor_id, - ROW_NUMBER() OVER (PARTITION BY dh.raw ORDER BY dr.tx_id DESC) AS rank + drep_distr.*, + ROW_NUMBER() OVER (PARTITION BY drep_hash.id ORDER BY drep_distr.epoch_no DESC) AS rn FROM - drep_hash dh - JOIN - drep_registration dr ON dh.id = dr.drep_hash_id - JOIN - tx t ON dr.tx_id = t.id - JOIN - block b ON t.block_id = b.id - WHERE - dr.deposit >= 0 - GROUP BY - dh.raw, - b.epoch_no, - dr.voting_anchor_id, - dr.deposit, - dr.tx_id + drep_distr + JOIN drep_hash ON drep_hash.id = drep_distr.hash_id ), -current_epoch AS ( - SELECT - Max(NO) AS no - FROM - epoch +CurrentEpoch AS ( + SELECT MAX(no) AS no FROM epoch ), -current_block AS ( - SELECT - Max(block_no) AS block_no - FROM - block +ActiveDRepBoundaryEpoch AS ( + SELECT epoch_no - drep_activity AS epoch_no FROM DRepActivity ), -unique_delegators AS ( +LatestVotingProcedure AS ( SELECT - count(DISTINCT (addr_id)) AS count + vp.*, + ROW_NUMBER() OVER (PARTITION BY drep_voter ORDER BY tx_id DESC) AS rn FROM - delegation_vote + voting_procedure vp ), -total_delegations AS ( +LatestVoteEpoch AS ( SELECT - count(*) AS count + block.epoch_no, + lvp.drep_voter AS drep_id FROM - delegation_vote + LatestVotingProcedure lvp + JOIN tx ON tx.id = lvp.tx_id + JOIN block ON block.id = tx.block_id + WHERE + lvp.rn = 1 ), -total_gov_action_proposals AS ( +RankedDRepRegistration AS ( SELECT - count(DISTINCT (tx_id, INDEX)) AS count + dr.id, + dr.drep_hash_id, + dr.deposit, + dr.voting_anchor_id, + ROW_NUMBER() OVER (PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id DESC) AS rn, + encode(tx.hash, 'hex') AS tx_hash, + block.epoch_no FROM - gov_action_proposal + drep_registration dr + JOIN tx ON tx.id = dr.tx_id + JOIN block ON block.id = tx.block_id +), +TotalRegisteredDReps AS ( + SELECT COUNT(DISTINCT dh.raw) AS unique_registrations + FROM drep_registration dr + JOIN drep_hash dh ON dr.drep_hash_id = dh.id + WHERE dr.deposit > 0 +), +TotalActiveDReps AS ( + SELECT COUNT(DISTINCT RankedDRepRegistration.drep_hash_id) AS unique_active_drep_registrations + FROM RankedDRepRegistration + LEFT JOIN LatestVoteEpoch lve ON lve.drep_id = RankedDRepRegistration.drep_hash_id + WHERE + (RankedDRepRegistration.epoch_no >= (SELECT epoch_no FROM ActiveDRepBoundaryEpoch)) + OR (lve.epoch_no >= (SELECT epoch_no FROM ActiveDRepBoundaryEpoch)) ), -total_drep_votes AS ( +TotalInactiveDReps AS ( SELECT - count(*) AS count - FROM - voting_procedure - WHERE - voter_role = 'DRep' -), -total_stake_controlled_by_dreps AS ( + TotalRegisteredDReps.unique_registrations - TotalActiveDReps.unique_active_drep_registrations AS total_inactive_dreps + FROM TotalRegisteredDReps + CROSS JOIN TotalActiveDReps +), +TotalActiveCIP119CompliantDReps AS ( + SELECT COUNT(DISTINCT RankedDRepRegistration.drep_hash_id) AS unique_active_cip119_compliant_drep_registrations + FROM RankedDRepRegistration + JOIN voting_anchor va ON va.id = RankedDRepRegistration.voting_anchor_id + JOIN off_chain_vote_data ocvd ON ocvd.voting_anchor_id = va.id + JOIN off_chain_vote_drep_data ocvdd ON ocvdd.off_chain_vote_data_id = ocvd.id + WHERE ocvdd.given_name IS NOT NULL + AND (RankedDRepRegistration.epoch_no >= (SELECT epoch_no FROM ActiveDRepBoundaryEpoch) + OR (EXISTS ( + SELECT 1 FROM LatestVoteEpoch lve + WHERE lve.drep_id = RankedDRepRegistration.drep_hash_id + AND lve.epoch_no >= (SELECT epoch_no FROM ActiveDRepBoundaryEpoch) + ))) +), +TotalStakeControlledByActiveDReps AS ( SELECT - SUM(dd.amount)::bigint AS total + COALESCE(SUM(dd.amount), 0)::bigint AS total FROM - drep_distr dd + drep_hash dh + LEFT JOIN DRepDistr dd ON dd.hash_id = dh.id AND dd.rn = 1 + LEFT JOIN RankedDRepRegistration rd ON dd.hash_id = rd.drep_hash_id AND rd.rn = 1 + LEFT JOIN LatestVoteEpoch lve ON lve.drep_id = dh.id + CROSS JOIN DRepActivity WHERE - dd.epoch_no = (SELECT no FROM current_epoch) + dd.epoch_no = (SELECT no FROM CurrentEpoch) + AND COALESCE(rd.deposit, 0) >= 0 + AND ((DRepActivity.epoch_no - GREATEST(COALESCE(lve.epoch_no, 0), COALESCE(rd.epoch_no, 0))) <= DRepActivity.drep_activity) ), -total_stake_controlled_by_spos AS ( - SELECT - SUM(ps.stake)::bigint AS total - FROM - pool_stat ps - WHERE - ps.epoch_no = (SELECT no FROM current_epoch) +CurrentBlock AS ( + SELECT MAX(block_no) AS block_no FROM block ), -total_registered_direct_voters AS ( - SELECT - COUNT(DISTINCT dh.raw) AS unique_direct_voters - FROM - drep_registration dr - JOIN - drep_hash dh - ON - dr.drep_hash_id = dh.id - LEFT JOIN - voting_anchor va - ON - dr.voting_anchor_id = va.id - WHERE - dr.deposit > 0 - AND va.url IS NULL +UniqueDelegators AS ( + SELECT COUNT(DISTINCT addr_id) AS count FROM delegation_vote ), -total_registered_dreps AS ( - SELECT - count(DISTINCT dh.raw) AS unique_registrations - FROM - drep_registration dr - JOIN - drep_hash dh - ON - dr.drep_hash_id = dh.id - WHERE - dr.deposit > 0 +TotalDelegations AS ( + SELECT COUNT(*) AS count FROM delegation_vote +), +TotalGovActionProposals AS ( + SELECT COUNT(DISTINCT (tx_id, INDEX)) AS count FROM gov_action_proposal ), -total_active_dreps AS ( +TotalDRepVotes AS ( + SELECT COUNT(*) AS count FROM voting_procedure WHERE voter_role = 'DRep' +), +TotalStakeControlledBySPOs AS ( + SELECT SUM(ps.stake)::bigint AS total FROM pool_stat ps WHERE ps.epoch_no = (SELECT no FROM CurrentEpoch) +), +LatestExistingVotingAnchor AS ( SELECT - count(DISTINCT drep_hash_raw) AS unique_active_drep_registrations - FROM - RankedDRep + subquery.drep_registration_id, + subquery.drep_hash_id, + subquery.voting_anchor_id, + subquery.url, + subquery.metadata_hash, + subquery.ocvd_id + FROM ( + SELECT + dr.id AS drep_registration_id, + dr.drep_hash_id, + va.id AS voting_anchor_id, + va.url, + encode(va.data_hash, 'hex') AS metadata_hash, + ocvd.id AS ocvd_id, + ROW_NUMBER() OVER (PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id DESC) AS rn + FROM + drep_registration dr + JOIN voting_anchor va ON dr.voting_anchor_id = va.id + JOIN off_chain_vote_data ocvd ON va.id = ocvd.voting_anchor_id + WHERE + ocvd.voting_anchor_id IS NOT NULL + ) subquery WHERE - epoch_no >= (SELECT epoch_no FROM active_drep_boundary_epoch) - AND rank = 1 + subquery.rn = 1 ), -total_inactive_dreps AS ( +HasNonDeregisterVotingAnchor AS ( SELECT - total_registered_dreps.unique_registrations - total_active_dreps.unique_active_drep_registrations AS total_inactive_dreps + dr.drep_hash_id, + EXISTS ( + SELECT 1 + FROM drep_registration dr_sub + WHERE + dr_sub.drep_hash_id = dr.drep_hash_id + AND dr_sub.voting_anchor_id IS NULL + AND COALESCE(dr_sub.deposit, 0) >= 0 + ) AS value FROM - total_registered_dreps - CROSS JOIN - total_active_dreps + drep_registration dr + GROUP BY + dr.drep_hash_id ), -total_active_cip119_compliant_dreps AS ( - SELECT - count(DISTINCT drep_hash_raw) AS unique_active_cip119_compliant_drep_registrations - FROM - RankedDRep - JOIN - voting_anchor va on va.id = RankedDRep.voting_anchor_id - JOIN off_chain_vote_data ocvd on ocvd.voting_anchor_id = va.id - JOIN off_chain_vote_drep_data ocvdd on ocvdd.off_chain_vote_data_id = ocvd.id - WHERE - -- given_name is the only compulsory field in CIP-119 - ocvdd.given_name IS NOT NULL - AND - epoch_no >= (SELECT epoch_no FROM active_drep_boundary_epoch) - AND - rank = 1 -), -always_abstain_voting_power AS ( - SELECT - coalesce(( - SELECT - amount - FROM drep_hash +TotalRegisteredDirectVoters AS ( + SELECT COUNT(DISTINCT rdr.drep_hash_id) AS unique_direct_voters + FROM RankedDRepRegistration rdr + LEFT JOIN LatestExistingVotingAnchor leva ON leva.drep_hash_id = rdr.drep_hash_id + LEFT JOIN HasNonDeregisterVotingAnchor hndva ON hndva.drep_hash_id = rdr.drep_hash_id + WHERE rdr.rn = 1 AND COALESCE(rdr.deposit, 0) >= 0 AND (leva.url IS NULL OR hndva.value = true) +), +AlwaysAbstainVotingPower AS ( + SELECT COALESCE((SELECT amount FROM drep_hash LEFT JOIN drep_distr ON drep_hash.id = drep_distr.hash_id - WHERE - drep_hash.view = 'drep_always_abstain' ORDER BY epoch_no DESC LIMIT 1), 0) AS amount + WHERE drep_hash.view = 'drep_always_abstain' + ORDER BY epoch_no DESC LIMIT 1), 0) AS amount ), -always_no_confidence_voting_power AS ( - SELECT - coalesce(( - SELECT - amount - FROM drep_hash +AlwaysNoConfidenceVotingPower AS ( + SELECT COALESCE((SELECT amount FROM drep_hash LEFT JOIN drep_distr ON drep_hash.id = drep_distr.hash_id - WHERE - drep_hash.view = 'drep_always_no_confidence' ORDER BY epoch_no DESC LIMIT 1), 0) AS amount + WHERE drep_hash.view = 'drep_always_no_confidence' + ORDER BY epoch_no DESC LIMIT 1), 0) AS amount +), +TotalDRepDistr AS ( + SELECT SUM(COALESCE(amount, 0))::bigint total_drep_distr FROM drep_distr where epoch_no = (SELECT no from CurrentEpoch) ) SELECT - current_epoch.no as epoch_no, - current_block.block_no, - unique_delegators.count as unique_delegators, - total_delegations.count as total_delegations, - total_gov_action_proposals.count as total_gov_action_proposals, - total_drep_votes.count as total_drep_votes, - total_registered_dreps.unique_registrations as total_registered_dreps, - COALESCE(total_stake_controlled_by_dreps.total, 0) as total_stake_controlled_by_dreps, - COALESCE(total_stake_controlled_by_spos.total, 0) as total_stake_controlled_by_spos, - total_active_dreps.unique_active_drep_registrations as total_active_dreps, - total_inactive_dreps.total_inactive_dreps as total_inactive_dreps, - total_active_cip119_compliant_dreps.unique_active_cip119_compliant_drep_registrations as total_active_cip119_compliant_dreps, - total_registered_direct_voters.unique_direct_voters as total_registered_direct_voters, - always_abstain_voting_power.amount as always_abstain_voting_power, - always_no_confidence_voting_power.amount as always_no_confidence_voting_power, - network_name -FROM - current_epoch - CROSS JOIN current_block - CROSS JOIN unique_delegators - CROSS JOIN total_delegations - CROSS JOIN total_gov_action_proposals - CROSS JOIN total_drep_votes - CROSS JOIN total_registered_dreps - CROSS JOIN total_stake_controlled_by_dreps - CROSS JOIN total_stake_controlled_by_spos - CROSS JOIN total_active_dreps - CROSS JOIN total_inactive_dreps - CROSS JOIN total_active_cip119_compliant_dreps - CROSS JOIN total_registered_direct_voters - CROSS JOIN always_abstain_voting_power - CROSS JOIN always_no_confidence_voting_power - CROSS JOIN meta -GROUP BY - current_epoch.no, - current_block.block_no, - unique_delegators.count, - total_delegations.count, - total_gov_action_proposals.count, - total_drep_votes.count, - total_registered_dreps.unique_registrations, - total_stake_controlled_by_dreps.total, - total_stake_controlled_by_spos.total, - total_active_dreps.unique_active_drep_registrations, - total_inactive_dreps.total_inactive_dreps, - total_active_cip119_compliant_dreps.unique_active_cip119_compliant_drep_registrations, - total_registered_direct_voters.unique_direct_voters, - always_abstain_voting_power.amount, - always_no_confidence_voting_power.amount, - network_name; - + CurrentEpoch.no AS epoch_no, + CurrentBlock.block_no, + UniqueDelegators.count AS unique_delegators, + TotalDelegations.count AS total_delegations, + TotalGovActionProposals.count AS total_gov_action_proposals, + TotalDRepVotes.count AS total_drep_votes, + TotalRegisteredDReps.unique_registrations AS total_registered_dreps, + TotalDRepDistr.total_drep_distr, + COALESCE(TotalStakeControlledByActiveDReps.total, 0) + COALESCE(AlwaysNoConfidenceVotingPower.amount, 0) AS total_stake_controlled_by_active_dreps, + COALESCE(TotalStakeControlledBySPOs.total, 0) AS total_stake_controlled_by_spos, + TotalActiveDReps.unique_active_drep_registrations AS total_active_dreps, + TotalInactiveDReps.total_inactive_dreps AS total_inactive_dreps, + TotalActiveCIP119CompliantDReps.unique_active_cip119_compliant_drep_registrations AS total_active_cip119_compliant_dreps, + TotalRegisteredDirectVoters.unique_direct_voters AS total_registered_direct_voters, + AlwaysAbstainVotingPower.amount AS always_abstain_voting_power, + AlwaysNoConfidenceVotingPower.amount AS always_no_confidence_voting_power, + meta.network_name +FROM CurrentEpoch +CROSS JOIN CurrentBlock +CROSS JOIN UniqueDelegators +CROSS JOIN TotalDRepDistr +CROSS JOIN TotalDelegations +CROSS JOIN TotalGovActionProposals +CROSS JOIN TotalDRepVotes +CROSS JOIN TotalRegisteredDReps +CROSS JOIN TotalStakeControlledByActiveDReps +CROSS JOIN TotalStakeControlledBySPOs +CROSS JOIN TotalActiveDReps +CROSS JOIN TotalInactiveDReps +CROSS JOIN TotalActiveCIP119CompliantDReps +CROSS JOIN TotalRegisteredDirectVoters +CROSS JOIN AlwaysAbstainVotingPower +CROSS JOIN AlwaysNoConfidenceVotingPower +CROSS JOIN meta; diff --git a/govtool/backend/sql/list-proposals.sql b/govtool/backend/sql/list-proposals.sql index ad66a5c7e..cedc4e86d 100644 --- a/govtool/backend/sql/list-proposals.sql +++ b/govtool/backend/sql/list-proposals.sql @@ -100,24 +100,38 @@ RankedPoolVotes AS ( voting_procedure vp ), PoolVotes AS ( - SELECT - rpv.gov_action_proposal_id, - ps.epoch_no, - COUNT(DISTINCT CASE WHEN vote = 'Yes' THEN rpv.pool_voter ELSE 0 END) AS total_unique_votes, - COUNT(DISTINCT CASE WHEN vote = 'No' THEN rpv.pool_voter ELSE 0 END) AS total_unique_votes, - COUNT(DISTINCT CASE WHEN vote = 'Abstain' THEN rpv.pool_voter ELSE 0 END) AS total_unique_votes, - SUM(CASE WHEN rpv.vote = 'Yes' THEN ps.voting_power ELSE 0 END) AS poolYesVotes, - SUM(CASE WHEN rpv.vote = 'No' THEN ps.voting_power ELSE 0 END) AS poolNoVotes, - SUM(CASE WHEN rpv.vote = 'Abstain' THEN ps.voting_power ELSE 0 END) AS poolAbstainVotes - FROM - RankedPoolVotes rpv - JOIN - pool_stat ps - ON rpv.pool_voter = ps.pool_hash_id - WHERE - rpv.rn = 1 AND ps.epoch_no = (SELECT MAX(no) FROM epoch) - GROUP BY - rpv.gov_action_proposal_id, ps.epoch_no + SELECT + rpv.gov_action_proposal_id, + ps.epoch_no, + COUNT(DISTINCT CASE WHEN vote = 'Yes' THEN rpv.pool_voter ELSE 0 END) AS total_unique_votes, + COUNT(DISTINCT CASE WHEN vote = 'No' THEN rpv.pool_voter ELSE 0 END) AS total_unique_votes, + COUNT(DISTINCT CASE WHEN vote = 'Abstain' THEN rpv.pool_voter ELSE 0 END) AS total_unique_votes, + SUM(CASE WHEN rpv.vote = 'Yes' THEN ps.voting_power ELSE 0 END) AS poolYesVotes, + SUM(CASE WHEN rpv.vote = 'No' THEN ps.voting_power ELSE 0 END) AS poolNoVotes, + SUM(CASE WHEN rpv.vote = 'Abstain' THEN ps.voting_power ELSE 0 END) AS poolAbstainVotes + FROM + RankedPoolVotes rpv + JOIN + pool_stat ps + ON rpv.pool_voter = ps.pool_hash_id + WHERE + rpv.rn = 1 AND ps.epoch_no = (SELECT MAX(no) FROM epoch) + GROUP BY + rpv.gov_action_proposal_id, ps.epoch_no +), +RankedDRepVotes AS ( + SELECT + *, + ROW_NUMBER() OVER (PARTITION BY vp.drep_voter ORDER BY vp.tx_id DESC) AS rn + FROM + voting_procedure vp +), +RankedDRepRegistration AS ( + SELECT + *, + ROW_NUMBER() OVER (PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id DESC) AS rn + FROM + drep_registration dr ), CommitteeVotes AS ( SELECT @@ -225,19 +239,19 @@ SELECT off_chain_vote_gov_action_data.abstract, off_chain_vote_gov_action_data.motivation, off_chain_vote_gov_action_data.rationale, - COALESCE(SUM(ldd_drep.amount) FILTER (WHERE voting_procedure.vote::text = 'Yes'), 0) yes_votes, - COALESCE(SUM(ldd_drep.amount) FILTER (WHERE voting_procedure.vote::text = 'No'), 0) + ( - CASE WHEN gov_action_proposal.type = 'NoConfidence' OR gov_action_proposal.type = 'HardForkInitiation' THEN + COALESCE(SUM(ldd_drep.amount) FILTER (WHERE rdv.vote::text = 'Yes'), 0) + ( + CASE WHEN gov_action_proposal.type = 'NoConfidence' THEN + drep_voting_power.no_confidence + ELSE + 0 + END) yes_votes, + COALESCE(SUM(ldd_drep.amount) FILTER (WHERE rdv.vote::text = 'No'), 0) + ( + CASE WHEN gov_action_proposal.type = 'NoConfidence' THEN 0 ELSE drep_voting_power.no_confidence END) no_votes, - COALESCE(SUM(ldd_drep.amount) FILTER (WHERE voting_procedure.vote::text = 'Abstain'), 0) + ( - CASE WHEN gov_action_proposal.type = 'NoConfidence' OR gov_action_proposal.type = 'HardForkInitiation' THEN - 0 - ELSE - drep_voting_power.abstain - END) abstain_votes, + COALESCE(SUM(ldd_drep.amount) FILTER (WHERE rdv.vote::text = 'Abstain'), 0) abstain_votes, COALESCE(ps.poolYesVotes, 0) pool_yes_votes, COALESCE(ps.poolNoVotes, 0) pool_no_votes, COALESCE(ps.poolAbstainVotes, 0) pool_abstain_votes, @@ -260,8 +274,9 @@ FROM LEFT JOIN cost_model AS cost_model ON proposal_params.cost_model_id = cost_model.id LEFT JOIN PoolVotes ps ON gov_action_proposal.id = ps.gov_action_proposal_id LEFT JOIN CommitteeVotes cv ON gov_action_proposal.id = cv.gov_action_proposal_id - LEFT JOIN voting_procedure ON voting_procedure.gov_action_proposal_id = gov_action_proposal.id - LEFT JOIN LatestDrepDistr ldd_drep ON ldd_drep.hash_id = voting_procedure.drep_voter + LEFT JOIN RankedDRepVotes rdv ON rdv.gov_action_proposal_id = gov_action_proposal.id AND rdv.rn = 1 + LEFT JOIN RankedDRepRegistration rdr ON rdr.drep_hash_id = rdv.drep_voter AND COALESCE(rdr.deposit, 0) >= 0 AND rdr.rn = 1 + LEFT JOIN LatestDrepDistr ldd_drep ON ldd_drep.hash_id = rdr.drep_hash_id AND ldd_drep.epoch_no = latest_epoch.no LEFT JOIN gov_action_proposal AS prev_gov_action ON gov_action_proposal.prev_gov_action_proposal = prev_gov_action.id LEFT JOIN tx AS prev_gov_action_tx ON prev_gov_action.tx_id = prev_gov_action_tx.id diff --git a/govtool/backend/src/VVA/API.hs b/govtool/backend/src/VVA/API.hs index 89b37cc3c..5424b32e7 100644 --- a/govtool/backend/src/VVA/API.hs +++ b/govtool/backend/src/VVA/API.hs @@ -278,14 +278,19 @@ getVotes :: App m => HexText -> [GovernanceActionType] -> Maybe GovernanceAction getVotes (unHexText -> dRepId) selectedTypes sortMode mSearch = do CacheEnv {dRepGetVotesCache} <- asks vvaCache (votes, proposals) <- cacheRequest dRepGetVotesCache dRepId $ DRep.getVotes dRepId [] + let voteMap = Map.fromList $ map (\vote@Types.Vote {..} -> (voteProposalId, vote)) votes + processedProposals <- filter (isProposalSearchedFor mSearch) <$> mapSortAndFilterProposals selectedTypes sortMode proposals + return $ [ VoteResponse - { voteResponseVote = voteToResponse (voteMap Map.! read (unpack proposalResponseId)) + { voteResponseVote = voteToResponse vote , voteResponseProposal = proposalResponse } | proposalResponse@ProposalResponse{proposalResponseId} <- processedProposals + , let proposalIdInt = read (unpack proposalResponseId) :: Int + , Just vote <- [Map.lookup (toInteger proposalIdInt) voteMap] ] drepInfo :: App m => HexText -> m DRepInfoResponse @@ -436,6 +441,7 @@ getNetworkMetrics = do , getNetworkMetricsResponseTotalGovernanceActions = networkMetricsTotalGovernanceActions , getNetworkMetricsResponseTotalDRepVotes = networkMetricsTotalDRepVotes , getNetworkMetricsResponseTotalRegisteredDReps = networkMetricsTotalRegisteredDReps + , getNetworkMetricsResponseTotalDRepDistr = networkMetricsTotalDRepDistr , getNetworkMetricsResponseTotalStakeControlledByDReps = networkMetricsTotalStakeControlledByDReps , getNetworkMetricsResponseTotalStakeControlledBySPOs = networkMetricsTotalStakeControlledBySPOs , getNetworkMetricsResponseTotalActiveDReps = networkMetricsTotalActiveDReps diff --git a/govtool/backend/src/VVA/API/Types.hs b/govtool/backend/src/VVA/API/Types.hs index 2181bd676..947c7ac53 100644 --- a/govtool/backend/src/VVA/API/Types.hs +++ b/govtool/backend/src/VVA/API/Types.hs @@ -884,6 +884,7 @@ data GetNetworkMetricsResponse , getNetworkMetricsResponseTotalGovernanceActions :: Integer , getNetworkMetricsResponseTotalDRepVotes :: Integer , getNetworkMetricsResponseTotalRegisteredDReps :: Integer + , getNetworkMetricsResponseTotalDRepDistr :: Integer , getNetworkMetricsResponseTotalStakeControlledByDReps :: Integer , getNetworkMetricsResponseTotalStakeControlledBySPOs :: Integer , getNetworkMetricsResponseTotalActiveDReps :: Integer @@ -907,6 +908,7 @@ exampleGetNetworkMetricsResponse = <> "\"totalGovernanceActions\": 0," <> "\"totalDRepVotes\": 0," <> "\"totalRegisteredDReps\": 0," + <> "\"totalDRepDistr\": 0," <> "\"totalStakeControlledByDReps\": 0," <> "\"totalStakeControlledBySPOs\": 0," <> "\"totalActiveDReps\": 0," diff --git a/govtool/backend/src/VVA/Network.hs b/govtool/backend/src/VVA/Network.hs index 8b844d5d2..7187bb9b5 100644 --- a/govtool/backend/src/VVA/Network.hs +++ b/govtool/backend/src/VVA/Network.hs @@ -42,6 +42,7 @@ networkMetrics = withPool $ \conn -> do , total_gov_action_proposals , total_drep_votes , total_registered_dreps + , total_drep_distr , total_stake_controlled_by_dreps , total_stake_controlled_by_spos , total_active_dreps @@ -60,6 +61,7 @@ networkMetrics = withPool $ \conn -> do total_gov_action_proposals total_drep_votes total_registered_dreps + total_drep_distr total_stake_controlled_by_dreps total_stake_controlled_by_spos total_active_dreps diff --git a/govtool/backend/src/VVA/Types.hs b/govtool/backend/src/VVA/Types.hs index 7a8fa3722..b07d87fcb 100644 --- a/govtool/backend/src/VVA/Types.hs +++ b/govtool/backend/src/VVA/Types.hs @@ -225,6 +225,7 @@ data NetworkMetrics , networkMetricsTotalGovernanceActions :: Integer , networkMetricsTotalDRepVotes :: Integer , networkMetricsTotalRegisteredDReps :: Integer + , networkMetricsTotalDRepDistr :: Integer , networkMetricsTotalStakeControlledByDReps :: Integer , networkMetricsTotalStakeControlledBySPOs :: Integer , networkMetricsTotalActiveDReps :: Integer diff --git a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx index 5bd37ba6c..00b3dbe02 100644 --- a/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx +++ b/govtool/frontend/src/components/molecules/GovernanceActionDetailsCardVotes.tsx @@ -58,7 +58,7 @@ export const GovernanceActionDetailsCardVotes = ({ isInProgress={isInProgress} /> ) : ( - + )} ); diff --git a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx index c063d8887..b4c1afe53 100644 --- a/govtool/frontend/src/components/molecules/VotesSubmitted.tsx +++ b/govtool/frontend/src/components/molecules/VotesSubmitted.tsx @@ -10,12 +10,15 @@ import { } from "@utils"; import { SubmittedVotesData } from "@models"; import { useFeatureFlag, useAppContext } from "@/context"; +import { GovernanceActionType } from "@/types/governanceAction"; type Props = { + type: GovernanceActionType; votes: SubmittedVotesData; }; export const VotesSubmitted = ({ + type: govActionType, votes: { dRepYesVotes, dRepAbstainVotes, @@ -47,23 +50,38 @@ export const VotesSubmitted = ({ const { t } = useTranslation(); const { epochParams, networkMetrics } = useAppContext(); + // Coming from be + // Equal to: total active drep stake + auto no-confidence stake const totalStakeControlledByDReps = - networkMetrics?.totalStakeControlledByDReps ?? 0; + (networkMetrics?.totalStakeControlledByDReps ?? 0) - + // As this being voted for the action becomes part of the total active stake + dRepAbstainVotes; - const totalDRepVotes = totalStakeControlledByDReps - ? totalStakeControlledByDReps - dRepAbstainVotes - : undefined; - const dRepYesVotesPercentage = totalDRepVotes - ? (dRepYesVotes / totalDRepVotes) * 100 + // Governance action abstain votesa + auto abstain votes + const totalAbstainVotes = + dRepAbstainVotes + (networkMetrics?.alwaysAbstainVotingPower ?? 0); + + // TODO: Move this logic to backend + const dRepYesVotesPercentage = totalStakeControlledByDReps + ? (dRepYesVotes / totalStakeControlledByDReps) * 100 : undefined; - const dRepNoVotesPercentage = totalDRepVotes - ? (dRepNoVotes / totalDRepVotes) * 100 + + const dRepNoVotesPercentage = totalStakeControlledByDReps + ? (dRepNoVotes / totalStakeControlledByDReps) * 100 : undefined; + const dRepNotVotedVotes = totalStakeControlledByDReps ? totalStakeControlledByDReps - - dRepYesVotes - - dRepNoVotes - - dRepAbstainVotes + (dRepYesVotes - + // As this is already added on backend + (govActionType === GovernanceActionType.NoConfidence + ? networkMetrics?.alwaysNoConfidenceVotingPower ?? 0 + : 0)) - + (dRepNoVotes - + // As this is already added on backend + (govActionType === GovernanceActionType.NoConfidence + ? 0 + : networkMetrics?.alwaysNoConfidenceVotingPower ?? 0)) : undefined; const dRepNotVotedVotesPercentage = 100 - (dRepYesVotesPercentage ?? 0) - (dRepNoVotesPercentage ?? 0); @@ -136,7 +154,7 @@ export const VotesSubmitted = ({ yesVotesPercentage={dRepYesVotesPercentage} noVotes={dRepNoVotes} noVotesPercentage={dRepNoVotesPercentage} - abstainVotes={dRepAbstainVotes} + abstainVotes={totalAbstainVotes} notVotedVotes={dRepNotVotedVotes} notVotedPercentage={dRepNotVotedVotesPercentage} threshold={(() => { diff --git a/govtool/frontend/src/components/organisms/DRepDetailsCard.tsx b/govtool/frontend/src/components/organisms/DRepDetailsCard.tsx index ac4c5b2fe..de812fd9a 100644 --- a/govtool/frontend/src/components/organisms/DRepDetailsCard.tsx +++ b/govtool/frontend/src/components/organisms/DRepDetailsCard.tsx @@ -48,6 +48,7 @@ export const DRepDetailsCard = ({ drepId, votingPower, isScriptBased, + metadataHash, } = dRepData; const groupedReferences = references?.reduce>( @@ -235,6 +236,53 @@ export const DRepDetailsCard = ({ /> )} + {url && ( + + + + {url} + + link + + + )} + {metadataHash && ( + + + + )} )} {/* CIP-119 DATA END */} diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx index ac0613661..1f204407b 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCard.tsx @@ -36,7 +36,7 @@ export const GovernanceActionDetailsCard = ({ sx={{ borderRadius: "20px", display: "grid", - gridTemplateColumns: isOneColumn ? undefined : "0.6fr 0.4fr", + gridTemplateColumns: isOneColumn ? undefined : "0.55fr 0.45fr", mt: "12px", width: "100%", position: "relative", diff --git a/govtool/frontend/src/components/organisms/Modal/SubmittedVotesModal.tsx b/govtool/frontend/src/components/organisms/Modal/SubmittedVotesModal.tsx index 1f3f7b6f5..32fb6889f 100644 --- a/govtool/frontend/src/components/organisms/Modal/SubmittedVotesModal.tsx +++ b/govtool/frontend/src/components/organisms/Modal/SubmittedVotesModal.tsx @@ -23,7 +23,7 @@ export const SubmittedVotesModal = forwardRef((_, ref) => { > - + diff --git a/govtool/frontend/src/i18n/locales/en.json b/govtool/frontend/src/i18n/locales/en.json index 370a74adf..eb9926f8e 100644 --- a/govtool/frontend/src/i18n/locales/en.json +++ b/govtool/frontend/src/i18n/locales/en.json @@ -328,7 +328,9 @@ "references": "References", "referenceDescription": "Description", "referenceDescriptionHelpfulText": "Limit: 80 characters", - "referenceURL": "URL" + "referenceURL": "URL", + "metadataUrl": "Metadata URL", + "metadataHash": "Metadata Hash" }, "errors": { "tooLongUrl": "Url must be less than 128 bytes", diff --git a/govtool/frontend/src/models/api.ts b/govtool/frontend/src/models/api.ts index 5deceeba3..bfa233d1e 100644 --- a/govtool/frontend/src/models/api.ts +++ b/govtool/frontend/src/models/api.ts @@ -87,6 +87,7 @@ export type NetworkMetrics = { totalGovernanceActions: number; totalDRepVotes: number; totalRegisteredDReps: number; + totalDRepDistr: number; totalStakeControlledByDReps: number; totalStakeControlledBySPOs: number; totalActiveDReps: number; diff --git a/govtool/frontend/yarn.lock b/govtool/frontend/yarn.lock index e0f4aac63..f24f8e693 100644 --- a/govtool/frontend/yarn.lock +++ b/govtool/frontend/yarn.lock @@ -1367,15 +1367,15 @@ resolved "https://registry.npmjs.org/@emurgo/cardano-serialization-lib-asmjs/-/cardano-serialization-lib-asmjs-12.1.1.tgz" integrity sha512-K3f28QUfLDJ7seO6MtKfMYtRm5ccf36TQ5yxyTmZqX1TA85MkriEdxqpgV9KLiLEA95emwnlvU2/WmlHMRPg1A== -"@esbuild/darwin-arm64@0.21.5": +"@esbuild/linux-x64@0.21.5": version "0.21.5" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz" - integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== -"@esbuild/darwin-arm64@0.24.2": +"@esbuild/linux-x64@0.24.2": version "0.24.2" - resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz" - integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz" + integrity sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.1" @@ -2162,10 +2162,15 @@ resolved "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz" integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== -"@parcel/watcher-darwin-arm64@2.5.0": +"@parcel/watcher-linux-x64-glibc@2.5.0": version "2.5.0" - resolved "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz" - integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw== + resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz" + integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw== + +"@parcel/watcher-linux-x64-musl@2.5.0": + version "2.5.0" + resolved "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz" + integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA== "@parcel/watcher@^2.4.1": version "2.5.0" @@ -2278,10 +2283,15 @@ estree-walker "^2.0.2" picomatch "^4.0.2" -"@rollup/rollup-darwin-arm64@4.27.4": +"@rollup/rollup-linux-x64-gnu@4.27.4": version "4.27.4" - resolved "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz" - integrity sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q== + resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz" + integrity sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q== + +"@rollup/rollup-linux-x64-musl@4.27.4": + version "4.27.4" + resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz" + integrity sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw== "@rtsao/scc@^1.1.0": version "1.1.0" @@ -2860,10 +2870,15 @@ "@svgr/plugin-svgo" "^5.5.0" loader-utils "^2.0.0" -"@swc/core-darwin-arm64@1.9.3": +"@swc/core-linux-x64-gnu@1.9.3": version "1.9.3" - resolved "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.9.3.tgz" - integrity sha512-hGfl/KTic/QY4tB9DkTbNuxy5cV4IeejpPD4zo+Lzt4iLlDWIeANL4Fkg67FiVceNJboqg48CUX+APhDHO5G1w== + resolved "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.9.3.tgz" + integrity sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w== + +"@swc/core-linux-x64-musl@1.9.3": + version "1.9.3" + resolved "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.9.3.tgz" + integrity sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg== "@swc/core@*", "@swc/core@^1.5.22", "@swc/core@^1.7.26": version "1.9.3" @@ -7169,16 +7184,6 @@ fs@^0.0.1-security: resolved "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz" integrity sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w== -fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: - version "2.3.3" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz" - integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== - -fsevents@2.3.2: - version "2.3.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" diff --git a/tests/govtool-frontend/playwright/lib/_mock/treasuryProposedGAs.json b/tests/govtool-frontend/playwright/lib/_mock/treasuryProposedGAs.json new file mode 100644 index 000000000..29c84a6c3 --- /dev/null +++ b/tests/govtool-frontend/playwright/lib/_mock/treasuryProposedGAs.json @@ -0,0 +1,1362 @@ +{ + "data": [ + { + "id": 17204, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3316", + "createdAt": "2025-01-30T11:29:33.731Z", + "updatedAt": "2025-01-30T11:29:33.731Z", + "user_govtool_username": "korbin46_1738235936720", + "content": { + "id": 17183, + "attributes": { + "proposal_id": "17204", + "prop_rev_active": true, + "prop_abstract": "cura amoveo dedico complectus crastinus", + "prop_motivation": "tergeo amor volva aqua arma", + "prop_rationale": "pel ocer desidero blandior cultura", + "gov_action_type_id": "2", + "prop_name": "Statua cimentarius depraedor advenio alveus corrupti.", + "is_draft": false, + "user_id": "3316", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-30T11:29:33.760Z", + "updatedAt": "2025-01-30T11:29:33.760Z", + "proposal_links": [ + { + "id": 17030, + "prop_link": "https://slight-clarity.org/", + "prop_link_text": "euphoric-barium" + } + ], + "proposal_withdrawals": [ + { + "id": 15358, + "prop_receiving_address": "stake1uqqv758up00900wk9k7xlud8ljtj5zjw4hczht34034kyrgwcsvsk", + "prop_amount": 621 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17183, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3306", + "createdAt": "2025-01-30T09:52:59.102Z", + "updatedAt": "2025-01-30T09:52:59.102Z", + "user_govtool_username": "myah43_1738230167776", + "content": { + "id": 17162, + "attributes": { + "proposal_id": "17183", + "prop_rev_active": true, + "prop_abstract": "copia venia voluptates accusator varius", + "prop_motivation": "currus paulatim canto veritas cognatus", + "prop_rationale": "volutabrum comburo vitium cogito coniuratio", + "gov_action_type_id": "2", + "prop_name": "Verbera cubicularis acer argentum benevolentia laudantium.", + "is_draft": false, + "user_id": "3306", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-30T09:52:59.130Z", + "updatedAt": "2025-01-30T09:52:59.130Z", + "proposal_links": [ + { + "id": 17009, + "prop_link": "https://playful-size.name/", + "prop_link_text": "bare-spiderling" + } + ], + "proposal_withdrawals": [ + { + "id": 15337, + "prop_receiving_address": "stake1up6c3kuuf9ccy6nq5qr7j2k52m8csg54yhm50d5r5x8lg4q2vvhnc", + "prop_amount": 879 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17181, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-30T09:43:38.887Z", + "updatedAt": "2025-01-30T09:43:38.887Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17160, + "attributes": { + "proposal_id": "17181", + "prop_rev_active": true, + "prop_abstract": "desparatus arma speculum ver admoneo", + "prop_motivation": "adstringo ultio capto cado tertius", + "prop_rationale": "tergum stella depraedor tamen vigilo", + "gov_action_type_id": "2", + "prop_name": "Patruus articulus atqui temporibus curtus cernuus.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-30T09:43:38.914Z", + "updatedAt": "2025-01-30T09:43:38.914Z", + "proposal_links": [ + { + "id": 17007, + "prop_link": "https://sore-channel.org/", + "prop_link_text": "enormous-summary" + } + ], + "proposal_withdrawals": [ + { + "id": 15335, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 883 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17162, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3294", + "createdAt": "2025-01-30T07:33:43.674Z", + "updatedAt": "2025-01-30T07:33:43.674Z", + "user_govtool_username": "albin_cormier42_1738221335719", + "content": { + "id": 17141, + "attributes": { + "proposal_id": "17162", + "prop_rev_active": true, + "prop_abstract": "sonitus decimus succedo aedificium repellendus", + "prop_motivation": "aetas verto capitulus copiose tabgo", + "prop_rationale": "nostrum desidero confero ambulo supellex", + "gov_action_type_id": "2", + "prop_name": "Crebro animadverto pecto trado alo alienus.", + "is_draft": false, + "user_id": "3294", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-30T07:33:43.699Z", + "updatedAt": "2025-01-30T07:33:43.699Z", + "proposal_links": [ + { + "id": 16988, + "prop_link": "https://optimistic-bill.biz/", + "prop_link_text": "tedious-regulation" + } + ], + "proposal_withdrawals": [ + { + "id": 15316, + "prop_receiving_address": "stake1uz924cfl9ag6ga4ah0c3mrgrley6pelvxrc30augvmqgwmqenqge9", + "prop_amount": 619 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17160, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-30T07:16:26.480Z", + "updatedAt": "2025-01-30T07:16:26.480Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17139, + "attributes": { + "proposal_id": "17160", + "prop_rev_active": true, + "prop_abstract": "cimentarius summopere voco natus quod", + "prop_motivation": "corporis solum constans amplus tertius", + "prop_rationale": "illum quasi cilicium debitis avaritia", + "gov_action_type_id": "2", + "prop_name": "Depono distinctio adhuc animi tres cum.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-30T07:16:26.514Z", + "updatedAt": "2025-01-30T07:16:26.514Z", + "proposal_links": [ + { + "id": 16986, + "prop_link": "https://far-off-leaf.org", + "prop_link_text": "unconscious-plunger" + } + ], + "proposal_withdrawals": [ + { + "id": 15314, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 494 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17158, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-30T07:07:07.606Z", + "updatedAt": "2025-01-30T07:07:07.606Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17137, + "attributes": { + "proposal_id": "17158", + "prop_rev_active": true, + "prop_abstract": "curriculum in caste alioqui amoveo", + "prop_motivation": "substantia angustus subvenio argentum tum", + "prop_rationale": "acervus reiciendis fuga tenetur aedificium", + "gov_action_type_id": "2", + "prop_name": "Copia comprehendo cognomen quam conspergo absorbeo.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-30T07:07:07.632Z", + "updatedAt": "2025-01-30T07:07:07.632Z", + "proposal_links": [ + { + "id": 16984, + "prop_link": "https://powerless-theology.net/", + "prop_link_text": "informal-bratwurst" + } + ], + "proposal_withdrawals": [ + { + "id": 15312, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 128 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17157, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3293", + "createdAt": "2025-01-30T07:05:45.314Z", + "updatedAt": "2025-01-30T07:05:45.314Z", + "user_govtool_username": "barney.kreiger_1738220473415", + "content": { + "id": 17136, + "attributes": { + "proposal_id": "17157", + "prop_rev_active": true, + "prop_abstract": "sulum charisma astrum tabernus coerceo", + "prop_motivation": "capitulus virgo spectaculum defendo voluntarius", + "prop_rationale": "denego tremo callide traho avaritia", + "gov_action_type_id": "2", + "prop_name": "Tendo ago dolores tandem ultra adaugeo.", + "is_draft": false, + "user_id": "3293", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-30T07:05:45.343Z", + "updatedAt": "2025-01-30T07:05:45.343Z", + "proposal_links": [ + { + "id": 16983, + "prop_link": "https://utter-memo.net", + "prop_link_text": "thunderous-cofactor" + } + ], + "proposal_withdrawals": [ + { + "id": 15311, + "prop_receiving_address": "stake1upmqsvunjaczvfyck7zqwptk56z2ty7smfmga5zvcduwz4cc9cq6v", + "prop_amount": 202 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17147, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3277", + "createdAt": "2025-01-29T16:43:47.743Z", + "updatedAt": "2025-01-29T16:43:47.743Z", + "user_govtool_username": "jacey45_1738168202436", + "content": { + "id": 17126, + "attributes": { + "proposal_id": "17147", + "prop_rev_active": true, + "prop_abstract": "averto vulariter absque tametsi thermae", + "prop_motivation": "pax aestivus aro causa cariosus", + "prop_rationale": "appositus beneficium avarus certus carpo", + "gov_action_type_id": "2", + "prop_name": "Amo aranea cibo alo vehemens tamquam.", + "is_draft": false, + "user_id": "3277", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T16:43:47.772Z", + "updatedAt": "2025-01-29T16:43:47.772Z", + "proposal_links": [ + { + "id": 16974, + "prop_link": "https://polished-sideboard.name/", + "prop_link_text": "worthwhile-foodstuffs" + } + ], + "proposal_withdrawals": [ + { + "id": 15301, + "prop_receiving_address": "stake1uqpsyvc8lcxvmt3xzrq09cmgv6hxmezdychhery9ctvt0xsw2zsvg", + "prop_amount": 441 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17133, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3277", + "createdAt": "2025-01-29T16:40:10.956Z", + "updatedAt": "2025-01-29T16:40:10.956Z", + "user_govtool_username": "jacey45_1738168202436", + "content": { + "id": 17112, + "attributes": { + "proposal_id": "17133", + "prop_rev_active": true, + "prop_abstract": "aeger cibus acer amaritudo incidunt", + "prop_motivation": "provident maiores quia deorsum suscipio", + "prop_rationale": "conatus culpa sulum alveus cum", + "gov_action_type_id": "2", + "prop_name": "Termes apostolus sonitus sustineo architecto demo.", + "is_draft": false, + "user_id": "3277", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T16:40:10.982Z", + "updatedAt": "2025-01-29T16:40:10.982Z", + "proposal_links": [ + { + "id": 16960, + "prop_link": "https://functional-chick.net/", + "prop_link_text": "tasty-symbol" + } + ], + "proposal_withdrawals": [ + { + "id": 15287, + "prop_receiving_address": "stake1uqpsyvc8lcxvmt3xzrq09cmgv6hxmezdychhery9ctvt0xsw2zsvg", + "prop_amount": 529 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17131, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-29T16:30:52.083Z", + "updatedAt": "2025-01-29T16:30:52.083Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17110, + "attributes": { + "proposal_id": "17131", + "prop_rev_active": true, + "prop_abstract": "uxor vulnus defendo dolorem vacuus", + "prop_motivation": "turbo quod eligendi veritas ait", + "prop_rationale": "apparatus ultra stabilis celo sol", + "gov_action_type_id": "2", + "prop_name": "Curriculum inventore patrocinor vaco similique solvo.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T16:30:52.111Z", + "updatedAt": "2025-01-29T16:30:52.111Z", + "proposal_links": [ + { + "id": 16958, + "prop_link": "https://reflecting-gain.name/", + "prop_link_text": "repentant-oregano" + } + ], + "proposal_withdrawals": [ + { + "id": 15285, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 910 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17112, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3264", + "createdAt": "2025-01-29T16:17:23.432Z", + "updatedAt": "2025-01-29T16:17:23.432Z", + "user_govtool_username": "gilda26_1738166760447", + "content": { + "id": 17091, + "attributes": { + "proposal_id": "17112", + "prop_rev_active": true, + "prop_abstract": "pax iure carus ocer bellum", + "prop_motivation": "aqua vapulus demens cogo carus", + "prop_rationale": "tamdiu statua accusamus distinctio animus", + "gov_action_type_id": "2", + "prop_name": "Careo terreo careo odio carus temptatio.", + "is_draft": false, + "user_id": "3264", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T16:17:23.462Z", + "updatedAt": "2025-01-29T16:17:23.462Z", + "proposal_links": [ + { + "id": 16939, + "prop_link": "https://palatable-grand.info", + "prop_link_text": "infinite-deviation" + } + ], + "proposal_withdrawals": [ + { + "id": 15266, + "prop_receiving_address": "stake1uz7vg8mjrh8hnvsdsh4gzw3affghkpexfmzj69yra0mn5hqncy7kh", + "prop_amount": 682 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17110, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-29T16:06:48.927Z", + "updatedAt": "2025-01-29T16:06:48.927Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17089, + "attributes": { + "proposal_id": "17110", + "prop_rev_active": true, + "prop_abstract": "velum cito sperno adimpleo cruciamentum", + "prop_motivation": "adipisci vicinus eius copia debitis", + "prop_rationale": "illum deduco succedo coniuratio tamen", + "gov_action_type_id": "2", + "prop_name": "Censura vis utor aegrotatio vestrum tactus.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T16:06:48.953Z", + "updatedAt": "2025-01-29T16:06:48.953Z", + "proposal_links": [ + { + "id": 16937, + "prop_link": "https://radiant-donut.name", + "prop_link_text": "chilly-earth" + } + ], + "proposal_withdrawals": [ + { + "id": 15264, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 436 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17091, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3249", + "createdAt": "2025-01-29T13:29:34.484Z", + "updatedAt": "2025-01-29T13:29:34.484Z", + "user_govtool_username": "jimmy_deckow21_1738156299677", + "content": { + "id": 17070, + "attributes": { + "proposal_id": "17091", + "prop_rev_active": true, + "prop_abstract": "trucido tolero amplitudo arcesso bellicus", + "prop_motivation": "cauda amita itaque dolorem expedita", + "prop_rationale": "bene denego patrocinor iusto baiulus", + "gov_action_type_id": "2", + "prop_name": "Barba adnuo repellat incidunt quas aufero.", + "is_draft": false, + "user_id": "3249", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T13:29:34.516Z", + "updatedAt": "2025-01-29T13:29:34.516Z", + "proposal_links": [ + { + "id": 16918, + "prop_link": "https://admirable-prizefight.com", + "prop_link_text": "some-main" + } + ], + "proposal_withdrawals": [ + { + "id": 15245, + "prop_receiving_address": "stake1urqlur423v8w95kle28ftm3mt3jpwg6vx2fmlh4xk0pyntq0t2x03", + "prop_amount": 716 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17070, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3242", + "createdAt": "2025-01-29T13:25:44.278Z", + "updatedAt": "2025-01-29T13:25:44.278Z", + "user_govtool_username": "felicity_johns_1738156022811", + "content": { + "id": 17049, + "attributes": { + "proposal_id": "17070", + "prop_rev_active": true, + "prop_abstract": "vis caelestis vir arcus venio", + "prop_motivation": "statua voluptates admoveo concedo turba", + "prop_rationale": "abutor acsi sollers nihil ara", + "gov_action_type_id": "2", + "prop_name": "Currus uxor carpo teres varius testimonium.", + "is_draft": false, + "user_id": "3242", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T13:25:44.305Z", + "updatedAt": "2025-01-29T13:25:44.305Z", + "proposal_links": [ + { + "id": 16897, + "prop_link": "https://hollow-onset.info/", + "prop_link_text": "used-jacket" + } + ], + "proposal_withdrawals": [ + { + "id": 15224, + "prop_receiving_address": "stake1urvcy7mtk5r509jejxxwz6ha80w646yxaxaeq32a6fm39fs48p22e", + "prop_amount": 649 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17068, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-29T13:12:34.653Z", + "updatedAt": "2025-01-29T13:12:34.653Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17047, + "attributes": { + "proposal_id": "17068", + "prop_rev_active": true, + "prop_abstract": "utrum debeo callide templum corona", + "prop_motivation": "aranea ventus verto pauci provident", + "prop_rationale": "laudantium placeat deputo vix tabernus", + "gov_action_type_id": "2", + "prop_name": "Repellat magnam conturbo comitatus pecco patior.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T13:12:34.691Z", + "updatedAt": "2025-01-29T13:12:34.691Z", + "proposal_links": [ + { + "id": 16895, + "prop_link": "https://insistent-hypochondria.info/", + "prop_link_text": "raw-tamale" + } + ], + "proposal_withdrawals": [ + { + "id": 15222, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 779 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17066, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-29T13:07:52.762Z", + "updatedAt": "2025-01-29T13:07:52.762Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17045, + "attributes": { + "proposal_id": "17066", + "prop_rev_active": true, + "prop_abstract": "cernuus quam theologus denique aegre", + "prop_motivation": "admitto odit statua veritatis utilis", + "prop_rationale": "tum sui aurum speculum repellat", + "gov_action_type_id": "2", + "prop_name": "Curis tersus confero stella cerno celebrer.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T13:07:52.792Z", + "updatedAt": "2025-01-29T13:07:52.792Z", + "proposal_links": [ + { + "id": 16893, + "prop_link": "https://previous-fisherman.info/", + "prop_link_text": "leafy-hobby" + } + ], + "proposal_withdrawals": [ + { + "id": 15220, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 807 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17064, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-29T12:52:21.000Z", + "updatedAt": "2025-01-29T12:52:21.000Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17043, + "attributes": { + "proposal_id": "17064", + "prop_rev_active": true, + "prop_abstract": "anser quibusdam ab conventus creator", + "prop_motivation": "acerbitas atrocitas commodo libero apto", + "prop_rationale": "vilitas nobis desidero trepide certe", + "gov_action_type_id": "2", + "prop_name": "Casso statua ad credo aurum curo.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T12:52:21.027Z", + "updatedAt": "2025-01-29T12:52:21.027Z", + "proposal_links": [ + { + "id": 16891, + "prop_link": "https://mortified-faucet.info", + "prop_link_text": "super-sandbar" + } + ], + "proposal_withdrawals": [ + { + "id": 15218, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 581 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17062, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-29T12:52:00.202Z", + "updatedAt": "2025-01-29T12:52:00.202Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17041, + "attributes": { + "proposal_id": "17062", + "prop_rev_active": true, + "prop_abstract": "tabella aggredior basium ager summisse", + "prop_motivation": "blanditiis pax venio ter defaeco", + "prop_rationale": "ambitus coniuratio clementia complectus verto", + "gov_action_type_id": "2", + "prop_name": "Cerno tempora accusamus celo curvo iure.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T12:52:00.242Z", + "updatedAt": "2025-01-29T12:52:00.242Z", + "proposal_links": [ + { + "id": 16889, + "prop_link": "https://parched-catalogue.name", + "prop_link_text": "inborn-middleman" + } + ], + "proposal_withdrawals": [ + { + "id": 15216, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 436 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17043, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3215", + "createdAt": "2025-01-29T11:48:35.324Z", + "updatedAt": "2025-01-29T11:48:35.324Z", + "user_govtool_username": "carter_padberg53_1738150702304", + "content": { + "id": 17022, + "attributes": { + "proposal_id": "17043", + "prop_rev_active": true, + "prop_abstract": "utique surgo validus consuasor esse", + "prop_motivation": "aetas despecto aiunt avaritia conturbo", + "prop_rationale": "anser labore est uterque comes", + "gov_action_type_id": "2", + "prop_name": "Doloremque vacuus tricesimus id approbo adopto.", + "is_draft": false, + "user_id": "3215", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T11:48:35.354Z", + "updatedAt": "2025-01-29T11:48:35.354Z", + "proposal_links": [ + { + "id": 16870, + "prop_link": "https://heartfelt-economics.name/", + "prop_link_text": "gloomy-derby" + } + ], + "proposal_withdrawals": [ + { + "id": 15197, + "prop_receiving_address": "stake1uz862u3nh7nldmeakemr3pkxdr8xz6n39xkq3wtu2ptxhcsfghatd", + "prop_amount": 805 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17024, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "3206", + "createdAt": "2025-01-29T11:42:48.167Z", + "updatedAt": "2025-01-29T11:42:48.167Z", + "user_govtool_username": "ericka.ritchie_1738150365664", + "content": { + "id": 17003, + "attributes": { + "proposal_id": "17024", + "prop_rev_active": true, + "prop_abstract": "cicuta undique verto comparo vespillo", + "prop_motivation": "absorbeo videlicet cenaculum deprecator accusator", + "prop_rationale": "natus voco conicio baiulus condico", + "gov_action_type_id": "2", + "prop_name": "Undique subseco abundans aegre vulnus ambulo.", + "is_draft": false, + "user_id": "3206", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T11:42:48.193Z", + "updatedAt": "2025-01-29T11:42:48.193Z", + "proposal_links": [ + { + "id": 16851, + "prop_link": "https://meaty-gate.net", + "prop_link_text": "orange-widow" + } + ], + "proposal_withdrawals": [ + { + "id": 15178, + "prop_receiving_address": "stake1urf2xgpkh7u2kjee4uu5as2h24l2tefpc4xsnha8sdsj6uqhtgsyq", + "prop_amount": 722 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17022, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-29T11:39:16.069Z", + "updatedAt": "2025-01-29T11:39:16.069Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 17001, + "attributes": { + "proposal_id": "17022", + "prop_rev_active": true, + "prop_abstract": "amet culpo vicissitudo arca adulatio", + "prop_motivation": "calcar agnitio succurro taedium ipsa", + "prop_rationale": "summa terebro minima bis sordeo", + "gov_action_type_id": "2", + "prop_name": "Beatae anser stillicidium quam aperio vallum.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T11:39:16.105Z", + "updatedAt": "2025-01-29T11:39:16.105Z", + "proposal_links": [ + { + "id": 16849, + "prop_link": "https://good-spark.biz", + "prop_link_text": "surprised-identification" + } + ], + "proposal_withdrawals": [ + { + "id": 15176, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 961 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 17020, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-29T11:33:35.606Z", + "updatedAt": "2025-01-29T11:33:35.606Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 16999, + "attributes": { + "proposal_id": "17020", + "prop_rev_active": true, + "prop_abstract": "curtus capitulus tardus brevis vox", + "prop_motivation": "auditor et aduro aperio vulgivagus", + "prop_rationale": "conturbo tersus minus cubo aestivus", + "gov_action_type_id": "2", + "prop_name": "Cursus attonbitus deorsum tollo ciminatio villa.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T11:33:35.634Z", + "updatedAt": "2025-01-29T11:33:35.634Z", + "proposal_links": [ + { + "id": 16847, + "prop_link": "https://silent-container.biz", + "prop_link_text": "tense-facelift" + } + ], + "proposal_withdrawals": [ + { + "id": 15174, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 447 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 16999, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-29T09:24:17.156Z", + "updatedAt": "2025-01-29T09:24:17.156Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 16978, + "attributes": { + "proposal_id": "16999", + "prop_rev_active": true, + "prop_abstract": "crustulum versus vapulus aliquam circumvenio", + "prop_motivation": "adhuc thalassinus arx dolores curvo", + "prop_rationale": "valde bene acquiro stabilis officiis", + "gov_action_type_id": "2", + "prop_name": "Curvo adsuesco depereo statua vulticulus thorax.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-29T09:24:17.191Z", + "updatedAt": "2025-01-29T09:24:17.191Z", + "proposal_links": [ + { + "id": 16826, + "prop_link": "https://traumatic-eagle.org/", + "prop_link_text": "anchored-sake" + } + ], + "proposal_withdrawals": [ + { + "id": 15153, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 261 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 16958, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-28T17:48:59.554Z", + "updatedAt": "2025-01-28T17:48:59.554Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 16937, + "attributes": { + "proposal_id": "16958", + "prop_rev_active": true, + "prop_abstract": "complectus adflicto voro adipiscor volubilis", + "prop_motivation": "decimus quis adduco alo trans", + "prop_rationale": "ubi demitto crux trepide contego", + "gov_action_type_id": "2", + "prop_name": "Undique ambulo acerbitas ter tertius titulus.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-28T17:48:59.585Z", + "updatedAt": "2025-01-28T17:48:59.585Z", + "proposal_links": [ + { + "id": 16785, + "prop_link": "https://testy-neuron.biz/", + "prop_link_text": "exemplary-selling" + } + ], + "proposal_withdrawals": [ + { + "id": 15112, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 563 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + }, + { + "id": 16957, + "attributes": { + "prop_likes": 0, + "prop_dislikes": 0, + "prop_comments_number": 0, + "user_id": "1876", + "createdAt": "2025-01-28T17:45:28.363Z", + "updatedAt": "2025-01-28T17:45:28.363Z", + "user_govtool_username": "cordie_stark_1732618876964", + "content": { + "id": 16936, + "attributes": { + "proposal_id": "16957", + "prop_rev_active": true, + "prop_abstract": "sol deprimo bonus adicio tamdiu", + "prop_motivation": "accusamus creta vir ara aetas", + "prop_rationale": "ultio degero exercitationem adfero vae", + "gov_action_type_id": "2", + "prop_name": "Animus virgo averto suscipio agnosco decerno.", + "is_draft": false, + "user_id": "1876", + "prop_submitted": false, + "prop_submission_tx_hash": null, + "prop_submission_date": null, + "createdAt": "2025-01-28T17:45:28.387Z", + "updatedAt": "2025-01-28T17:45:28.387Z", + "proposal_links": [ + { + "id": 16784, + "prop_link": "https://klutzy-light.biz", + "prop_link_text": "boring-closing" + } + ], + "proposal_withdrawals": [ + { + "id": 15111, + "prop_receiving_address": "stake1up8xg8ur80nsymvefrlk4dshjyfrze4pep843l289f4n8fgynegq0", + "prop_amount": 555 + } + ], + "gov_action_type": { + "id": 2, + "attributes": { + "gov_action_type_name": "Treasury", + "createdAt": "2024-08-21T21:40:32.586Z", + "updatedAt": "2024-08-21T21:40:33.850Z", + "publishedAt": "2024-08-21T21:40:33.843Z" + } + } + } + } + } + } + ], + "meta": { + "pagination": { + "page": 1, + "pageSize": 25, + "pageCount": 138, + "total": 3437 + } + } +} diff --git a/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts b/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts index d9f7459b4..3f28e2eaf 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/adaFormat.ts @@ -2,13 +2,21 @@ const LOVELACE = 1000000; export const correctVoteAdaFormat = ( lovelace: number | undefined, - locale: string | undefined = undefined + precision = 2 ) => { if (lovelace) { const ada = lovelace / LOVELACE; - return ada.toLocaleString(locale, { - maximumFractionDigits: 3, - }); + if (ada < 1000) + return ada.toLocaleString("en-us", { + maximumFractionDigits: precision, + }); + const suffixes = ["k", "M", "B", "T"]; + const divisors = [1000, 1000000, 1000000000, 1000000000000]; + for (let i = 0; i < suffixes.length; i++) { + if (ada < divisors[i] * 1000) { + return (ada / divisors[i]).toFixed(precision) + suffixes[i]; + } + } } return "0"; }; diff --git a/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts b/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts index 6a0d78b28..0d2a28be2 100644 --- a/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts @@ -79,6 +79,8 @@ export default class DRepDirectoryPage { (filter) => !filters.includes(filter) ); + await this.page.waitForTimeout(4_000); // wait for the dRep list to render properly + const dRepList = await this.getAllListedDReps(); for (const filter of excludedFilters) { diff --git a/tests/govtool-frontend/playwright/lib/pages/governanceActionDetailsPage.ts b/tests/govtool-frontend/playwright/lib/pages/governanceActionDetailsPage.ts index 600d8818f..4199b7f99 100644 --- a/tests/govtool-frontend/playwright/lib/pages/governanceActionDetailsPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/governanceActionDetailsPage.ts @@ -1,7 +1,8 @@ import environments from "@constants/environments"; import { downloadMetadata } from "@helpers/metadata"; -import { Download, Page } from "@playwright/test"; +import { Download, Page, Response } from "@playwright/test"; import metadataBucketService from "@services/metadataBucketService"; +import { IProposal } from "@types"; import { withTxConfirmation } from "lib/transaction.decorator"; export default class GovernanceActionDetailsPage { @@ -39,6 +40,9 @@ export default class GovernanceActionDetailsPage { readonly dRepYesVotes = this.page.getByTestId("submitted-votes-dReps-yes"); readonly dRepNoVotes = this.page.getByTestId("submitted-votes-dReps-no"); + readonly dRepNotVoted = this.page.getByTestId( + "submitted-votes-dReps-notVoted" + ); readonly dRepAbstainVotes = this.page.getByTestId( "submitted-votes-dReps-abstain" ); @@ -97,6 +101,29 @@ export default class GovernanceActionDetailsPage { await this.voteBtn.click(); } + async getDRepNotVoted( + proposal: IProposal, + metricsResponsePromise: Promise + ): Promise { + const metricsResponses = await Promise.resolve(metricsResponsePromise); + const totalStakeControlledByDReps = await metricsResponses + .json() + .then((data) => data.totalStakeControlledByDReps); + + if ( + totalStakeControlledByDReps && + typeof totalStakeControlledByDReps === "number" + ) { + const dRepNotVoted = + totalStakeControlledByDReps - + proposal.dRepYesVotes - + proposal.dRepAbstainVotes - + proposal.dRepNoVotes; + + return dRepNotVoted; + } + } + async downloadVoteMetadata() { const download: Download = await this.page.waitForEvent("download"); return downloadMetadata(download); diff --git a/tests/govtool-frontend/playwright/lib/pages/proposalDiscussionPage.ts b/tests/govtool-frontend/playwright/lib/pages/proposalDiscussionPage.ts index b44d9ca46..a8dbaaa12 100644 --- a/tests/govtool-frontend/playwright/lib/pages/proposalDiscussionPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/proposalDiscussionPage.ts @@ -92,10 +92,11 @@ export default class ProposalDiscussionPage { filters: string[], validateFunction: (proposalCard: any, filters: string[]) => Promise ) { + await this.page.waitForTimeout(4_000); // wait for the proposals to load // single filter for (const filter of filters) { await this.filterProposalByNames([filter]); - await this.validateFilters(filters, validateFunction); + await this.validateFilters([filter], validateFunction); await this.unFilterProposalByNames([filter]); } diff --git a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts index e168afd2a..4036d80ba 100644 --- a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts @@ -150,6 +150,10 @@ test.describe("Check vote count", () => { ) ); + const metricsResponsePromise = page.waitForResponse((response) => + response.url().includes(`network/metrics`) + ); + const governanceActionsPage = new GovernanceActionsPage(page); await governanceActionsPage.goto(); @@ -180,6 +184,11 @@ test.describe("Check vote count", () => { `${proposalToCheck.txHash}#${proposalToCheck.index}` ); + const dRepNotVoted = await govActionDetailsPage.getDRepNotVoted( + proposalToCheck, + metricsResponsePromise + ); + await govActionDetailsPage.showVotesBtn.click(); // check dRep votes @@ -193,6 +202,12 @@ test.describe("Check vote count", () => { await expect(govActionDetailsPage.dRepNoVotes).toHaveText( `₳ ${correctVoteAdaFormat(proposalToCheck.dRepNoVotes)}` ); + + if (dRepNotVoted) { + await expect(govActionDetailsPage.dRepNotVoted).toHaveText( + `₳ ${correctVoteAdaFormat(dRepNotVoted)}` + ); + } } // check sPos votes diff --git a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts index 8b08c93c0..9de7c222d 100644 --- a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts +++ b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts @@ -45,6 +45,10 @@ test("4K. Should display correct vote counts on governance details page for disc ) ); + const metricsResponsePromise = page.waitForResponse((response) => + response.url().includes(`network/metrics`) + ); + const governanceActionsPage = new GovernanceActionsPage(page); await governanceActionsPage.goto(); const responses = await Promise.all(responsesPromise); @@ -71,6 +75,11 @@ test("4K. Should display correct vote counts on governance details page for disc `${proposalToCheck.txHash}#${proposalToCheck.index}` ); + const dRepNotVoted = await govActionDetailsPage.getDRepNotVoted( + proposalToCheck, + metricsResponsePromise + ); + // check dRep votes if (await areDRepVoteTotalsDisplayed(proposalToCheck)) { await expect(govActionDetailsPage.dRepYesVotes).toHaveText( @@ -82,6 +91,12 @@ test("4K. Should display correct vote counts on governance details page for disc await expect(govActionDetailsPage.dRepNoVotes).toHaveText( `₳ ${correctVoteAdaFormat(proposalToCheck.dRepNoVotes)}` ); + + if (dRepNotVoted) { + await expect(govActionDetailsPage.dRepNotVoted).toHaveText( + `₳ ${correctVoteAdaFormat(dRepNotVoted)}` + ); + } } // check sPos votes if (await areSPOVoteTotalsDisplayed(proposalToCheck)) { diff --git a/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts index ac81002bf..1cfd89e32 100644 --- a/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts @@ -96,13 +96,18 @@ test.describe("Proposal checks", () => { }); test.describe("Validate provide context about vote", () => { + const characters = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; test("5D_1. Should accept valid data in provide context", async () => { await govActionDetailsPage.contextBtn.click(); await expect(govActionDetailsPage.contextInput).toBeVisible(); for (let i = 0; i < 100; i++) { - const randomContext = faker.lorem.paragraph(2); + const randomContext = faker.string.fromCharacters(characters, { + min: 1, + max: 9999, + }); await govActionDetailsPage.contextInput.fill(randomContext); expect(await govActionDetailsPage.contextInput.textContent()).toEqual( randomContext @@ -118,7 +123,7 @@ test.describe("Proposal checks", () => { await expect(govActionDetailsPage.contextInput).toBeVisible(); for (let i = 0; i < 100; i++) { - const randomContext = faker.lorem.paragraph(40); + const randomContext = faker.string.fromCharacters(characters, 10001); await govActionDetailsPage.contextInput.fill(randomContext); expect( await govActionDetailsPage.contextInput.textContent() diff --git a/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts index 36568db3b..bb35e0fbe 100644 --- a/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.loggedin.spec.ts @@ -68,7 +68,7 @@ test.describe("Proposal created logged in state", () => { await proposalDiscussionDetailsPage.addComment(randComment); await proposalDiscussionDetailsPage.replyComment(randReply); - await expect(page.getByText(randReply)).toBeVisible(); + await expect(page.getByText(randReply)).toBeVisible({ timeout: 15_000 }); }); }); @@ -122,12 +122,12 @@ test.describe("Proposal created with poll enabled (user auth)", () => { // vote must be changed await expect( page.getByTestId(`poll-${vote.toLowerCase()}-count`) - ).not.toHaveText(`${vote}: (0%)`); + ).toHaveText(`${vote}: (0%)`, { timeout: 15_000 }); // opposite of random choice vote const oppositeVote = pollVotes[pollVotes.length - 1 - choice]; await expect( page.getByTestId(`poll-${oppositeVote.toLowerCase()}-count`) - ).not.toHaveText(`${oppositeVote}: (100%)`); + ).toHaveText(`${oppositeVote}: (100%)`); }); }); diff --git a/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.spec.ts b/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.spec.ts index 4ad591246..c320cd668 100644 --- a/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.spec.ts +++ b/tests/govtool-frontend/playwright/tests/8-proposal-discussion/proposalDiscussion.spec.ts @@ -6,11 +6,13 @@ import { isBootStrapingPhase, skipIfNotHardFork } from "@helpers/cardano"; import ProposalDiscussionDetailsPage from "@pages/proposalDiscussionDetailsPage"; import ProposalDiscussionPage from "@pages/proposalDiscussionPage"; import { expect } from "@playwright/test"; +import { ProposalType } from "@types"; const mockProposal = require("../../lib/_mock/proposal.json"); const mockPoll = require("../../lib/_mock/proposalPoll.json"); const mockComments = require("../../lib/_mock/proposalComments.json"); const mockInfoProposedGA = require("../../lib/_mock/infoProposedGAs.json"); +const mockTreasuryProposal = require("../../lib/_mock/treasuryProposedGAs.json"); const PROPOSAL_TYPE_FILTERS = ["Info", "Treasury"]; const BOOTSTRAP_PROPOSAL_TYPE_FILTERS = ["Info"]; @@ -92,27 +94,49 @@ test("8C. Should search the list of proposed governance actions.", async ({ } }); -test("8D.Should show the view-all categorized proposed governance actions.", async ({ - page, +test("8D. Should show the view-all categorized proposed governance actions.", async ({ + browser, }) => { - await page.route("**/api/proposals?**", async (route) => { - return route.fulfill({ - body: JSON.stringify(mockInfoProposedGA), - }); - }); - - const proposalDiscussionPage = new ProposalDiscussionPage(page); - await proposalDiscussionPage.goto(); - - await proposalDiscussionPage.showAllBtn.click(); - - const proposalCards = await proposalDiscussionPage.getAllProposals(); - - for (const proposalCard of proposalCards) { - await expect( - proposalCard.getByTestId("governance-action-type") - ).toBeVisible(); - } + await Promise.all( + Object.entries({ + [ProposalType.info]: mockInfoProposedGA, + [ProposalType.treasury]: mockTreasuryProposal, + }).map(async ([proposalType, mockData]) => { + const requestUrl = `**/api/proposals?**`; + let requestHandled = 0; + + const context = await browser.newContext(); + const page = await context.newPage(); + + await page.route(requestUrl, async (route) => { + if (requestHandled < 2) { + requestHandled = requestHandled + 1; + return route.fulfill({ + body: JSON.stringify(mockData), + }); + } + return route.continue(); + }); + + const proposalDiscussionPage = new ProposalDiscussionPage(page); + await proposalDiscussionPage.goto(); + + await proposalDiscussionPage.filterBtn.click(); + await proposalDiscussionPage.filterProposalByNames([proposalType]); + // to close the filter menu + await proposalDiscussionPage.filterBtn.click({ force: true }); + + proposalDiscussionPage.showAllBtn.click(); + + const proposalCards = await proposalDiscussionPage.getAllProposals(); + + for (const proposalCard of proposalCards) { + await expect( + proposalCard.getByTestId("governance-action-type") + ).toHaveText(proposalType, { timeout: 20_000 }); + } + }) + ); }); test("8H. Should disable proposal interaction on a disconnected state.", async ({