Skip to content
Closed
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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ VeVote is a decentralized governance platform. This repository serves as the mon

Ensure your development environment is set up with the following:

- **Node.js (v18 or later):** [Download here](https://nodejs.org/en/download/package-manager) 📥
- **Node.js (v22 or later):** [Download here](https://nodejs.org/en/download/package-manager) 📥
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

README now states Node.js v22+, but the repo root package.json still declares engines.node as >=18. Please align these (either bump engines to >=22 if v22 is required, or keep README at v18+ if older Node versions are supported).

Suggested change
- **Node.js (v22 or later):** [Download here](https://nodejs.org/en/download/package-manager) 📥
- **Node.js (v18 or later):** [Download here](https://nodejs.org/en/download/package-manager) 📥

Copilot uses AI. Check for mistakes.
- **Yarn:** [Install here](https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable) 🧶
- **Docker (for containerization):** [Get Docker](https://docs.docker.com/get-docker/) 🐳
- **Hardhat (for smart contracts):** [Getting Started with Hardhat](https://hardhat.org/hardhat-runner/docs/getting-started) ⛑️
Expand Down
5 changes: 4 additions & 1 deletion apps/frontend/src/utils/proposals/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,11 @@ export const parseHistoricalProposals = async (
data?: HistoricalProposalData[],
): Promise<HistoricalProposalMerged[]> => {
if (!data) return [];
const ipfsFetches = data.map(d => getProposalsFromIpfs(d.description));

// Only fetch from IPFS if description (CID) is valid
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

The comment says the description/CID is "valid", but this only checks truthiness. If you intend to prevent bad CIDs, either validate/normalize the CID (e.g., trim, strip ipfs:// if applicable, or add a real CID validator) or adjust the comment to match the actual behavior.

Suggested change
// Only fetch from IPFS if description (CID) is valid
// Only fetch from IPFS if a description (CID) is present

Copilot uses AI. Check for mistakes.
const ipfsFetches = data.map(d =>
d.description ? getProposalsFromIpfs(d.description) : Promise.resolve({ ipfsHash: undefined }),
);
const ipfsDetails = await Promise.all(ipfsFetches);
Comment on lines +249 to 251
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

As written, a single failing IPFS request will reject Promise.all(ipfsFetches) and prevent parsing all historical proposals. To make this robust to invalid/missing CIDs or transient IPFS errors, consider catching per-item failures (or using Promise.allSettled) and falling back to empty IpfsDetails for that proposal.

Suggested change
d.description ? getProposalsFromIpfs(d.description) : Promise.resolve({ ipfsHash: undefined }),
);
const ipfsDetails = await Promise.all(ipfsFetches);
d.description ? getProposalsFromIpfs(d.description) : Promise.resolve({ ipfsHash: undefined } as IpfsDetails),
);
const ipfsResults = await Promise.allSettled(ipfsFetches);
const ipfsDetails = ipfsResults.map(result =>
result.status === "fulfilled" ? result.value : ({ ipfsHash: undefined } as IpfsDetails),
);

Copilot uses AI. Check for mistakes.

return data.map(d => {
Expand Down
5 changes: 4 additions & 1 deletion apps/frontend/src/utils/proposals/proposalQueriesUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ export const paginateProposals = (proposals: MergedProposal[], cursor?: string,
};

export const enrichProposalsWithData = async (proposals: FromEventsToProposalsReturnType) => {
const ipfsFetches = proposals.map(p => getProposalsFromIpfs(p.ipfsHash));
// Only fetch from IPFS if ipfsHash is valid
const ipfsFetches = proposals.map(p =>
p.ipfsHash ? getProposalsFromIpfs(p.ipfsHash) : Promise.resolve({ ipfsHash: undefined }),
);
Comment on lines +19 to +22
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

This presence-guard logic is now duplicated at call sites. A more maintainable approach is to have getProposalsFromIpfs short-circuit when the CID is missing/empty and return an empty IpfsDetails, so callers can simply map getProposalsFromIpfs(p.ipfsHash) without inline Promise.resolve(...) fallbacks.

Suggested change
// Only fetch from IPFS if ipfsHash is valid
const ipfsFetches = proposals.map(p =>
p.ipfsHash ? getProposalsFromIpfs(p.ipfsHash) : Promise.resolve({ ipfsHash: undefined }),
);
const ipfsFetches = proposals.map(p => getProposalsFromIpfs(p.ipfsHash));

Copilot uses AI. Check for mistakes.
Comment on lines +19 to +22
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

The comment says the hash is "valid", but the condition only checks truthiness (non-empty). Either validate the CID format (or trim whitespace before checking), or reword the comment to reflect that this is only a presence check.

Suggested change
// Only fetch from IPFS if ipfsHash is valid
const ipfsFetches = proposals.map(p =>
p.ipfsHash ? getProposalsFromIpfs(p.ipfsHash) : Promise.resolve({ ipfsHash: undefined }),
);
// Only fetch from IPFS if a non-empty ipfsHash is present
const ipfsFetches = proposals.map(p => {
const ipfsHash = p.ipfsHash?.trim();
return ipfsHash ? getProposalsFromIpfs(ipfsHash) : Promise.resolve({ ipfsHash: undefined });
});

Copilot uses AI. Check for mistakes.
const ipfsDetails: IpfsDetails[] = await Promise.all(ipfsFetches);
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

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

Promise.all(ipfsFetches) will reject if any getProposalsFromIpfs call fails (e.g., 404/timeout for an invalid CID), which can break the whole proposals load. Consider making each fetch resilient (wrap each call with .catch(() => ({})) / return a fallback IpfsDetails, or use Promise.allSettled and merge only fulfilled results).

Suggested change
const ipfsDetails: IpfsDetails[] = await Promise.all(ipfsFetches);
const ipfsDetailsResults = await Promise.allSettled(ipfsFetches);
const ipfsDetails: IpfsDetails[] = ipfsDetailsResults.map(result =>
result.status === "fulfilled" ? result.value : ({ ipfsHash: undefined } as IpfsDetails),
);

Copilot uses AI. Check for mistakes.

const mergedWithIpfs = mergeIpfsDetails(ipfsDetails, proposals);
Expand Down
Loading