Skip to content

Conversation

@zbcoding
Copy link

@zbcoding zbcoding commented Oct 5, 2025

Screenshot From 2025-10-05 12-45-30 Screenshot From 2025-10-05 12-45-40 Screenshot From 2025-10-05 12-45-51 Screenshot From 2025-10-05 12-46-33

Summary by CodeRabbit

  • New Features

    • Introduced PillarDAO app (Voting, Membership, Onboarding) with proposal browsing/pagination, WalletConnect onboarding/session management, membership display, and new UI components (animated title, copy-help, membership/voting/onboarding panels, shared styles, manifest).
  • Documentation

    • README note about needing a ReOwn account and Project ID for WalletConnect.
  • Tests

    • Added comprehensive PillarDAO UI tests and deterministic time handling in related tests.
  • Chores

    • Added Nixpacks and Cloudflare Pages (wrangler) deployment configs; enabled Polygon network support.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 5, 2025

Walkthrough

Adds a new PillarDAO sub-app (manifest, entry, panels, styles, components, tests), WalletConnect onboarding and session management, Snapshot voting integration with pagination and fallback, membership on-chain reads with localStorage caching, Polygon chain support, build/deploy configs (nixpacks, wrangler), and a README note about ReOwn WalletConnect Project ID.

Changes

Cohort / File(s) Summary
Docs
README.md
Added informational note instructing users to obtain a ReOwn account for WalletConnect features and copy the Project ID into .env.
Build & Deploy Config
nixpacks.toml, wrangler.jsonc
Added nixpacks.toml to build a Node static site and serve with Caddy; added wrangler.jsonc for Cloudflare Pages assets build config.
PillarDAO App Entry & Manifest
src/apps/pillardao/index.tsx, src/apps/pillardao/manifest.json
New PillarDaoApp entry using manifest-driven config, header image fallback, intro logic, and tabbed UI routing to panels.
PillarDAO Panels — Logic & Integration
src/apps/pillardao/components/OnboardingPanel.tsx, src/apps/pillardao/components/VotingPanel.tsx, src/apps/pillardao/components/MembershipPanel.tsx
New onboarding with WalletConnect session management and URI connect/validation; voting panel fetching and paginating Snapshot proposals with fallback voting URL handling; membership panel performing Polygon on-chain reads (balanceOf, tokenOfOwnerByIndex, deposit timestamp) with localStorage-backed cache and UI actions.
PillarDAO Components — Presentation
src/apps/pillardao/components/Styles.tsx, src/apps/pillardao/components/AnimatedTitle.tsx, src/apps/pillardao/components/CopyHelp.tsx
Added styled-components for PillarDAO UI, an AnimatedTitle per-letter animation component, and CopyHelp image-with-overlay component.
PillarDAO Tests
src/apps/pillardao/index.test.tsx
Added Vitest/RTL tests covering PillarDAO flows with mocks for AnimatedTitle, walletConnect, transaction-kit hooks, and wagmi.
Core Container — Chains
src/containers/Main.tsx
Added Polygon chain support to Wagmi configuration and enabled HTTP transport for Polygon.
Other Tests — Deterministic Time
src/apps/pillarx-app/.../LeftColumnTokenMarketDataRow.test.tsx, src/apps/pillarx-app/.../TokensWithMarketDataTile.test.tsx
Introduced Vitest fake timers and fixed system time in tests' beforeEach/afterEach for deterministic relative-time assertions.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant OnboardingPanel
  participant WalletConnect
  participant SessionStore as "WC Sessions"

  User->>OnboardingPanel: Paste wc: URI and click Connect
  OnboardingPanel->>WalletConnect: connect(wcUri)
  WalletConnect-->>SessionStore: create session
  WalletConnect-->>OnboardingPanel: success / error
  OnboardingPanel-->>User: display sessions or show error
Loading
sequenceDiagram
  autonumber
  actor User
  participant VotingPanel
  participant SnapshotAPI as "Snapshot GraphQL"

  User->>VotingPanel: View latest proposals
  VotingPanel->>SnapshotAPI: query(space: pillar-dao, first: N, skip: 0)
  SnapshotAPI-->>VotingPanel: proposals[]
  VotingPanel-->>User: render list
  User->>VotingPanel: View more
  VotingPanel->>SnapshotAPI: query(skip: next)
  SnapshotAPI-->>VotingPanel: proposals[] | empty
  VotingPanel-->>User: append or disable "more"
Loading
sequenceDiagram
  autonumber
  participant MembershipPanel
  participant LocalStorage
  participant PolygonRPC as "Polygon RPC"

  MembershipPanel->>LocalStorage: read cache(chainId,address,nft)
  alt connected
    MembershipPanel->>PolygonRPC: balanceOf(address)
    PolygonRPC-->>MembershipPanel: balance
    opt has NFT
      MembershipPanel->>PolygonRPC: tokenOfOwnerByIndex(address,0)
      MembershipPanel->>PolygonRPC: viewDepositTimestamp(tokenId)
      PolygonRPC-->>MembershipPanel: tokenId, timestamp
    end
  end
  MembershipPanel->>LocalStorage: write updated cache
  MembershipPanel-->>User: display membership info
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • IAmKio
  • RanaBug

Poem

"A rabbit taps the voting drum, hop-hop through the day,
Wallets link, tabs unfurl, proposals on display.
Cached tokens hum, Polygon trails we run,
Copy the Project ID, connect — the work is done.
Caddy serves builds while carrots cheer, hooray! 🥕"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description Check ⚠️ Warning The pull request lacks any description and does not follow the repository’s required template sections, leaving out the summary of changes, testing details, and types of changes, which makes it difficult to understand the scope and verification of the update. Please add a description that follows the repository template, including a brief summary of changes under “## Description,” details of how you tested the feature under “## How Has This Been Tested?,” and checkboxes for the types of changes introduced.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title “Add PillarDAO app” clearly and concisely describes the primary purpose of this changeset, which is to introduce the PillarDAO sub-application along with its components and configuration, making it easy for a reviewer to understand the main change at a glance.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@zbcoding zbcoding marked this pull request as ready for review October 5, 2025 17:47
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
src/apps/pillardao/index.test.tsx (1)

85-181: Reset global spies between tests

Since each scenario spies on global.fetch and window.open, we should call vi.restoreAllMocks() (or restore those specific spies) in an afterEach to return the globals to their original implementations. That keeps later tests or other suites from inheriting mocked behaviour and makes the suite safer to extend.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1931973 and fea1604.

⛔ Files ignored due to path filters (5)
  • src/apps/pillardao/icon.png is excluded by !**/*.png
  • src/apps/pillardao/images/pillar-dao-image.png is excluded by !**/*.png
  • src/apps/pillardao/images/pillar-dao-member-nft.png is excluded by !**/*.png
  • src/apps/pillardao/images/wallet-connect-example.png is excluded by !**/*.png
  • src/apps/pillardao/images/wallet-connect-sign-in-example.png is excluded by !**/*.png
📒 Files selected for processing (13)
  • README.md (1 hunks)
  • nixpacks.toml (1 hunks)
  • src/apps/pillardao/components/AnimatedTitle.tsx (1 hunks)
  • src/apps/pillardao/components/CopyHelp.tsx (1 hunks)
  • src/apps/pillardao/components/MembershipPanel.tsx (1 hunks)
  • src/apps/pillardao/components/OnboardingPanel.tsx (1 hunks)
  • src/apps/pillardao/components/Styles.tsx (1 hunks)
  • src/apps/pillardao/components/VotingPanel.tsx (1 hunks)
  • src/apps/pillardao/index.test.tsx (1 hunks)
  • src/apps/pillardao/index.tsx (1 hunks)
  • src/apps/pillardao/manifest.json (1 hunks)
  • src/containers/Main.tsx (3 hunks)
  • wrangler.jsonc (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
src/apps/pillardao/components/CopyHelp.tsx (1)
src/components/LandingPage/Img/index.jsx (1)
  • Img (27-43)
src/apps/pillardao/components/MembershipPanel.tsx (1)
src/apps/pillardao/components/Styles.tsx (4)
  • Section (3-6)
  • MembershipRow (29-37)
  • NftBox (39-50)
  • Row (8-10)
src/apps/pillardao/components/OnboardingPanel.tsx (2)
src/services/walletConnect.ts (1)
  • useWalletConnect (57-1297)
src/apps/pillardao/components/Styles.tsx (10)
  • Section (3-6)
  • Row (8-10)
  • SmallNote (12-16)
  • ConnectLayout (52-58)
  • ConnectAside (69-73)
  • ConnectInline (115-124)
  • RightAddon (93-99)
  • ConnectError (81-85)
  • WalletInfo (101-113)
  • SessionItem (184-196)
src/apps/pillardao/components/VotingPanel.tsx (1)
src/apps/pillardao/components/Styles.tsx (6)
  • Section (3-6)
  • Row (8-10)
  • ProposalsBox (18-27)
  • ProposalItem (126-140)
  • ProposalBody (148-160)
  • CollapseButton (162-175)
src/apps/pillardao/index.test.tsx (1)
src/theme/index.ts (1)
  • defaultTheme (83-163)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/apps/pillardao/components/MembershipPanel.tsx (2)

51-59: Consider extracting ABI definitions to a constants file.

The inline ABI definitions work correctly but could be moved to a shared constants file (e.g., abis.ts or constants.ts) to improve maintainability and reusability.


167-172: Consider extracting date formatting to a helper function.

The IIFE for date formatting could be extracted to improve readability and reusability.

Example refactor:

const formatMemberSince = (timestamp: number): string => {
  const d = new Date(timestamp * 1000);
  const date = d.toLocaleDateString();
  const time = d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
  return `${date} ${time}`;
};

Then use:

-              {effectiveDepositTs > 0
-                ? (() => {
-                    const d = new Date(effectiveDepositTs * 1000);
-                    const date = d.toLocaleDateString();
-                    const time = d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
-                    return `${date} ${time}`;
-                  })()
-                : '—'}
+              {effectiveDepositTs > 0 ? formatMemberSince(effectiveDepositTs) : '—'}
src/apps/pillarx-app/components/TokensWithMarketDataTile/test/TokensWithMarketDataTile.test.tsx (1)

207-207: Fix typo in test description.

The test description has a typo: "renders the right number of rowsd" should be "renders the right number of rows".

-  it('renders the right number of rowsd', () => {
+  it('renders the right number of rows', () => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fea1604 and 3d17af8.

⛔ Files ignored due to path filters (4)
  • src/apps/pillarx-app/components/EditorialTile/test/__snapshots__/EditorialTile.test.tsx.snap is excluded by !**/*.snap
  • src/apps/pillarx-app/components/TokenMarketDataRow/tests/__snapshots__/LeftColumnTokenMarketDataRow.test.tsx.snap is excluded by !**/*.snap
  • src/apps/pillarx-app/components/TokensWithMarketDataTile/test/__snapshots__/TokensWithMarketDataTile.test.tsx.snap is excluded by !**/*.snap
  • src/apps/the-exchange/components/CardsSwap/test/__snapshots__/CardSwap.test.tsx.snap is excluded by !**/*.snap
📒 Files selected for processing (5)
  • src/apps/pillardao/components/CopyHelp.tsx (1 hunks)
  • src/apps/pillardao/components/MembershipPanel.tsx (1 hunks)
  • src/apps/pillarx-app/components/TokenMarketDataRow/tests/LeftColumnTokenMarketDataRow.test.tsx (1 hunks)
  • src/apps/pillarx-app/components/TokensWithMarketDataTile/test/TokensWithMarketDataTile.test.tsx (2 hunks)
  • wrangler.jsonc (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • wrangler.jsonc
  • src/apps/pillardao/components/CopyHelp.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/apps/pillardao/components/MembershipPanel.tsx (1)
src/apps/pillardao/components/Styles.tsx (4)
  • Section (3-6)
  • MembershipRow (29-37)
  • NftBox (39-50)
  • Row (8-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: unit-tests
  • GitHub Check: lint
🔇 Additional comments (8)
src/apps/pillardao/components/MembershipPanel.tsx (6)

1-49: LGTM!

The component setup, types, and cache utilities are well-structured. The localStorage operations are properly guarded with try-catch blocks, and the cache loading effect has correct dependencies.


61-88: LGTM! Previous review concern has been addressed.

The zero-balance check in effectiveMembershipId (lines 82-83) properly handles the case when NFT balance drops to zero, preventing stale cached token IDs from being displayed. This addresses the concern raised in the previous review.


90-103: LGTM!

The deposit timestamp logic correctly prioritizes on-chain data over cached data, with a sensible fallback to zero.


105-110: LGTM!

The truncate utility is straightforward and handles edge cases appropriately.


112-127: Verify the eslint-disable justification.

The exhaustive-deps disable on line 127 appears intentional since cached is deliberately omitted to avoid infinite loops (the effect writes cache → updates state → triggers effect). However, this pattern can be fragile. Consider adding a comment explaining why cached is intentionally excluded from the dependency array.


129-149: LGTM!

The not-connected state UI is clear and appropriate.

src/apps/pillarx-app/components/TokensWithMarketDataTile/test/TokensWithMarketDataTile.test.tsx (1)

110-111: LGTM!

Proper cleanup with vi.useRealTimers() in afterEach ensures fake timers don't leak between tests.

src/apps/pillarx-app/components/TokenMarketDataRow/tests/LeftColumnTokenMarketDataRow.test.tsx (1)

36-44: Verify vi is imported or globally configured.

The fake timer setup and cleanup follow the same correct pattern as the other test file in this PR, ensuring deterministic relative time calculations. However, vi is referenced without a visible import statement.

The verification script from the first file (TokensWithMarketDataTile.test.tsx) will also confirm whether vi is properly available in this test file. If globals: true is configured in vitest.config.ts, no import is needed. Otherwise, add:

import { vi } from 'vitest';

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/apps/pillardao/components/MembershipPanel.tsx (1)

186-214: LGTM! Security improvements applied.

The window.open calls on lines 194 and 208 now include 'noopener' in the features string, addressing the security concern from the previous review.

The duplicate URL-building logic for Polygonscan and Blockscout could still be extracted into helper functions as suggested in the previous review, but this remains an optional refactor.

🧹 Nitpick comments (3)
src/apps/pillardao/components/MembershipPanel.tsx (3)

51-59: Consider extracting ABI definitions to a shared constants file.

The inline ABI definitions for ERC721 and membership contract methods could be extracted to a shared constants file (e.g., src/apps/pillardao/constants/abis.ts) if they're used in multiple components across the PillarDAO app. This would improve maintainability and reduce duplication.

Example structure:

// src/apps/pillardao/constants/abis.ts
export const ERC721_BALANCE_ABI = [
  { inputs: [{ name: 'owner', type: 'address' }], name: 'balanceOf', outputs: [{ type: 'uint256' }], stateMutability: 'view', type: 'function' },
] as const;

export const ERC721_ENUMERABLE_ABI = [
  { inputs: [{ name: 'owner', type: 'address' }, { name: 'index', type: 'uint256' }], name: 'tokenOfOwnerByIndex', outputs: [{ type: 'uint256' }], stateMutability: 'view', type: 'function' },
] as const;

export const MEMBERSHIP_TIMESTAMP_ABI = [
  { inputs: [{ name: 'member', type: 'address' }], name: 'viewDepositTimestamp', outputs: [{ type: 'uint256' }], stateMutability: 'view', type: 'function' },
] as const;

112-127: Consider validating balance before writing tokenId to cache.

When the NFT balance becomes zero, the nftFirstTokenRead query is disabled (line 77), but its data field may retain the previous token ID value. The cache writing effect uses this potentially stale value on line 114 without checking the current balance. While the UI correctly handles this case (due to the balance check in effectiveMembershipId), the cache could store stale membership data.

Apply this diff to ensure cache integrity:

   useEffect(() => {
     if (!isConnected) return;
+    const balance = nftBalanceRead?.data as bigint | undefined;
     const tokenId = nftFirstTokenRead?.data as bigint | undefined;
     const depositTs = Number(depositTsRead?.data || BigInt(0));
-    const hasUseful = (tokenId && tokenId > BigInt(0)) || depositTs > 0;
+    // Only consider tokenId valid if balance > 0
+    const validTokenId = balance && balance > BigInt(0) && tokenId && tokenId > BigInt(0) ? tokenId : undefined;
+    const hasUseful = validTokenId !== undefined || depositTs > 0;
     if (!hasUseful) return;
     writeMembershipCache({
       address: resolvedAddress,
       chainId,
       nftContract,
-      tokenId: tokenId && tokenId > BigInt(0) ? String(tokenId) : cached?.tokenId,
+      tokenId: validTokenId ? String(validTokenId) : undefined,
       depositTimestamp: depositTs > 0 ? depositTs : (cached?.depositTimestamp || 0),
       updatedAt: Date.now(),
     });
     // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [isConnected, chainId, resolvedAddress, nftContract, nftFirstTokenRead?.data, depositTsRead?.data]);
+  }, [isConnected, chainId, resolvedAddress, nftContract, nftBalanceRead?.data, nftFirstTokenRead?.data, depositTsRead?.data]);

167-172: Consider extracting date formatting to a helper function.

The inline IIFE for date formatting could be extracted to a helper function for improved readability and potential reuse.

const formatMembershipDate = (timestamp: number): string => {
  const d = new Date(timestamp * 1000);
  const date = d.toLocaleDateString();
  const time = d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
  return `${date} ${time}`;
};

// Usage:
<div className="value">
  {effectiveDepositTs > 0 ? formatMembershipDate(effectiveDepositTs) : '—'}
</div>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3d17af8 and e0b4896.

📒 Files selected for processing (3)
  • src/apps/pillardao/components/MembershipPanel.tsx (1 hunks)
  • src/apps/pillarx-app/components/TokenMarketDataRow/tests/LeftColumnTokenMarketDataRow.test.tsx (2 hunks)
  • src/apps/pillarx-app/components/TokensWithMarketDataTile/test/TokensWithMarketDataTile.test.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/apps/pillarx-app/components/TokensWithMarketDataTile/test/TokensWithMarketDataTile.test.tsx
  • src/apps/pillarx-app/components/TokenMarketDataRow/tests/LeftColumnTokenMarketDataRow.test.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
src/apps/pillardao/components/MembershipPanel.tsx (1)
src/apps/pillardao/components/Styles.tsx (4)
  • Section (3-6)
  • MembershipRow (29-37)
  • NftBox (39-50)
  • Row (8-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: unit-tests
  • GitHub Check: lint
🔇 Additional comments (3)
src/apps/pillardao/components/MembershipPanel.tsx (3)

61-97: LGTM! Contract reads are well-structured.

The contract read hooks are properly configured:

  • Balance read is always enabled when connected
  • Token read is conditionally enabled only when balance > 0 (good optimization)
  • Timestamp read is enabled when connected

The query enabling logic correctly prevents unnecessary on-chain calls when the wallet has no NFTs.


81-88: LGTM! Zero-balance handling is correct.

The balance check on line 82-83 correctly prevents using stale token IDs from cache or disabled queries when the wallet's NFT balance is zero. This fix (from previous review) ensures the UI accurately reflects membership status.


179-179: LGTM! Alt text has been added.

The NFT image now includes descriptive alt text ("PillarDAO Member NFT"), which improves accessibility for screen readers. This addresses the previous review comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant