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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ TENDERLY_ACCOUNT=
TENDERLY_PROJECT=
NEXT_PUBLIC_ENV=prod
NEXT_PUBLIC_ENABLE_GOVERNANCE=true
NEXT_PUBLIC_USE_GOVERNANCE_CACHE=true
NEXT_PUBLIC_GOVERNANCE_CACHE_URL=https://governance-cache-api.aave.com/graphql
Comment on lines +7 to +8
Copy link

Copilot AI Feb 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New governance cache env vars were added here, but the CI build action currently hard-codes NEXT_PUBLIC_* env values and does not include these (see .github/actions/build/action.yml). If CI/e2e builds should exercise the cache path, please plumb NEXT_PUBLIC_USE_GOVERNANCE_CACHE / NEXT_PUBLIC_GOVERNANCE_CACHE_URL into the relevant GitHub Actions/workflows as well.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these done on vercel

NEXT_PUBLIC_ENABLE_STAKING=true
NEXT_PUBLIC_API_BASEURL=https://aave-api-v2.aave.com
NEXT_PUBLIC_TRANSAK_APP_URL=https://global.transak.com
Expand Down
139 changes: 25 additions & 114 deletions pages/governance/ipfs-preview.governance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,145 +3,56 @@ import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { ContentContainer } from 'src/components/ContentContainer';
import { Meta } from 'src/components/Meta';
import { Proposal } from 'src/hooks/governance/useProposals';
import { MainLayout } from 'src/layouts/MainLayout';
import { ProposalOverview } from 'src/modules/governance/proposal/ProposalOverview';
import { ProposalTopPanel } from 'src/modules/governance/proposal/ProposalTopPanel';
import { ProposalBadgeState } from 'src/modules/governance/StateBadge';
import { ProposalLifecycleStep } from 'src/modules/governance/utils/formatProposal';
import { ProposalDetailDisplay } from 'src/modules/governance/types';
import { getProposalMetadata } from 'src/modules/governance/utils/getProposalMetadata';
import { ipfsGateway } from 'src/ui-config/governanceConfig';

// proposal metadata is fetched using the ipfs hash,
// but all other data is mocked so the page can be rendered
const mockProposal: Proposal = {
subgraphProposal: {
id: '',
creator: '',
accessLevel: '',
ipfsHash: '',
proposalMetadata: {
shortDescription: '',
aip: 1,
title: '',
author: '',
discussions: '',
ipfsHash: '',
description: '',
},
state: 1,
votingPortal: {
id: '',
votingMachineChainId: '',
votingMachine: '',
enabled: true,
},
votingConfig: {
id: '1',
cooldownBeforeVotingStart: '86400',
votingDuration: '259200',
yesThreshold: '320000',
yesNoDifferential: '80000',
minPropositionPower: '80000',
},
payloads: [
{
id: '0',
chainId: '1',
accessLevel: '1',
payloadsController: '',
},
],
transactions: {
id: '0',
created: {
id: '',
blockNumber: '',
timestamp: '',
},
active: null,
queued: null,
failed: null,
executed: null,
canceled: null,
},
votingDuration: null,
snapshotBlockHash: '0x0',
votes: {
forVotes: '0',
againstVotes: '0',
},
constants: {
id: '8',
precisionDivider: '1000000000000000000',
cooldownPeriod: '0',
expirationTime: '2592000',
cancellationFee: '50000000000000000',
},
},
votingMachineData: {
proposalData: {
id: '0',
sentToGovernance: false,
startTime: 0,
endTime: 0,
votingClosedAndSentTimestamp: 0,
forVotes: '0',
againstVotes: '0',
creationBlockNumber: 0,
votingClosedAndSentBlockNumber: 0,
},
votedInfo: {
support: false,
votingPower: '0',
},
strategy: '',
dataWarehouse: '',
votingAssets: [''],
hasRequiredRoots: false,
voteConfig: {
votingDuration: '0',
l1ProposalBlockHash: '',
},
state: 0,
},
payloadsData: [],
lifecycleState: ProposalLifecycleStep.Created,
const mockProposal: ProposalDetailDisplay = {
id: '',
title: '',
shortDescription: '',
description: '',
author: '',
discussions: '',
ipfsHash: '',
badgeState: ProposalBadgeState.Created,
votingInfo: {
voteInfo: {
forVotes: 0,
againstVotes: 0,
forPercent: 0,
againstPercent: 0,
quorum: '80000',
quorum: 0,
quorumReached: false,
currentDifferential: '0',
requiredDifferential: '320000',
currentDifferential: 0,
requiredDifferential: 0,
differentialReached: false,
isPassing: false,
},
};

export default function IpfsPreview() {
const router = useRouter();
const ipfsHash = router.query.ipfsHash as string;
const [loading, setLoading] = useState(true);
const [proposal, setProposal] = useState<Proposal>(mockProposal);
const [proposal, setProposal] = useState<ProposalDetailDisplay>(mockProposal);
const [error, setError] = useState(false);

async function fetchIpfs() {
try {
setLoading(true);
const proposalMetadata = await getProposalMetadata(ipfsHash, ipfsGateway);
setProposal((prev) => {
return {
...prev,
subgraphProposalb: {
...prev.subgraphProposal,
proposalMetadata,
},
};
});
setProposal((prev) => ({
...prev,
title: proposalMetadata.title,
shortDescription: proposalMetadata.shortDescription,
description: proposalMetadata.description,
author: proposalMetadata.author,
discussions: proposalMetadata.discussions || null,
ipfsHash: proposalMetadata.ipfsHash,
}));
setLoading(false);
} catch (e) {
console.error(e);
Expand All @@ -161,8 +72,8 @@ export default function IpfsPreview() {
{!loading && (
<Meta
imageUrl="https://app.aave.com/aaveMetaLogo-min.jpg"
title={proposal.subgraphProposal.proposalMetadata.title}
description={proposal.subgraphProposal.proposalMetadata.shortDescription}
title={proposal.title}
description={proposal.shortDescription}
/>
)}
<ProposalTopPanel />
Expand Down
43 changes: 26 additions & 17 deletions pages/governance/v3/proposal/index.governance.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ import { Grid } from '@mui/material';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import { Meta } from 'src/components/Meta';
import { useProposal } from 'src/hooks/governance/useProposal';
import { useProposalVotes } from 'src/hooks/governance/useProposalVotes';
import {
useGovernanceProposalDetail,
useGovernanceVotersSplit,
} from 'src/hooks/governance/useGovernanceProposals';
import { MainLayout } from 'src/layouts/MainLayout';
import { ProposalLifecycle } from 'src/modules/governance/proposal/ProposalLifecycle';
import { ProposalLifecycleCache } from 'src/modules/governance/proposal/ProposalLifecycleCache';
import { ProposalOverview } from 'src/modules/governance/proposal/ProposalOverview';
import { ProposalTopPanel } from 'src/modules/governance/proposal/ProposalTopPanel';
import { VoteInfo } from 'src/modules/governance/proposal/VoteInfo';
Expand All @@ -22,26 +25,27 @@ const GovVoteModal = dynamic(() =>
export default function ProposalPage() {
const { query } = useRouter();
const proposalId = Number(query.proposalId);

const {
data: proposal,
isLoading: proposalLoading,
error: newProposalError,
} = useProposal(proposalId);
error: proposalError,
} = useGovernanceProposalDetail(proposalId);

// For graph path, get votingChainId from rawProposal
const votingChainId = proposal?.rawProposal
? +proposal.rawProposal.subgraphProposal.votingPortal.votingMachineChainId
: undefined;

const proposalVotes = useProposalVotes({
proposalId,
votingChainId: proposal
? +proposal.subgraphProposal.votingPortal.votingMachineChainId
: undefined,
});
const voters = useGovernanceVotersSplit(proposalId, votingChainId);

return (
<>
{proposal && (
<Meta
imageUrl="https://app.aave.com/aaveMetaLogo-min.jpg"
title={proposal.subgraphProposal.proposalMetadata.title}
description={proposal.subgraphProposal.proposalMetadata.shortDescription}
title={proposal.title}
description={proposal.shortDescription}
/>
)}
<ProposalTopPanel />
Expand All @@ -50,19 +54,24 @@ export default function ProposalPage() {
<Grid container spacing={4}>
<Grid item xs={12} md={8}>
<ProposalOverview
proposal={proposal}
error={!!newProposalError}
proposal={proposal ?? undefined}
error={!!proposalError}
loading={proposalLoading}
/>
</Grid>
<Grid item xs={12} md={4}>
{proposal && <VoteInfo proposal={proposal} />}
{proposal?.rawProposal && <VoteInfo proposal={proposal.rawProposal} />}
<VotingResults
proposal={proposal}
proposalVotes={proposalVotes}
voters={voters}
loading={proposalLoading}
votesLoading={voters.isFetching}
/>
<ProposalLifecycle proposal={proposal} />
{proposal?.rawProposal ? (
<ProposalLifecycle proposal={proposal.rawProposal} />
) : proposal?.rawCacheDetail ? (
<ProposalLifecycleCache proposal={proposal.rawCacheDetail} />
) : null}
</Grid>
</Grid>
</ContentContainer>
Expand Down
Loading
Loading