Decentralized, Privacy-Preserving Voting Platform on Sui
Pollar is a decentralized application (dApp) that enables secure, anonymous, and verifiable voting experiences for communities and DAOs on the Sui blockchain. The platform combines Sui Seal encryption for privacy-preserving votes, zkLogin for seamless user onboarding, and NFT-gated access controls for exclusive voting rights.
Pollar revolutionizes on-chain governance by providing:
- Privacy-First Voting: Votes are encrypted using Mysten Seal before being submitted on-chain, ensuring voter anonymity while maintaining transparency of aggregate results
- Seamless Authentication: Users can sign in with Google accounts via zkLogin, eliminating the need to manage private keys
- NFT-Gated Polls: Create polls restricted to holders of specific NFT collections, perfect for DAO governance and community decisions
- Fully On-Chain Logic: Smart contracts written in Sui Move ensure security, speed, and transparency
- Real-Time Updates: Event-driven architecture provides instant poll updates and vote tallies
- Framework: React 18 with TypeScript
- Build Tool: Vite 7
- State Management: TanStack Query (React Query)
- Routing: React Router DOM 7
- UI Components: Radix UI Themes
- Styling: CSS Modules, GSAP for animations
- Charts: Recharts
- Testing: Vitest with React Testing Library
- Network: Sui Network (Testnet/Mainnet)
- Wallet: Mysten dApp Kit
- Authentication: Mysten Enoki (zkLogin)
- Encryption: Mysten Seal
- Smart Contracts: Sui Move
- Database: Supabase (PostgreSQL)
- Hosting: Walrus Sites
Before setting up Pollar, ensure you have the following installed:
- Node.js: Version 18 or higher
- Package Manager: npm or pnpm (pnpm recommended)
- Sui CLI: For smart contract development and deployment
- Git: For version control
git clone https://github.com/your-username/pollar.git
cd pollarNavigate to the frontend directory and install dependencies:
cd frontend
npm installOr if using pnpm:
pnpm installCreate a .env file in the frontend directory. You can use the .env copy.example file as a template:
cp ".env copy.example" .envEdit the .env file and configure the following environment variables:
Enoki Configuration
VITE_ENOKI_API_KEY: Your Enoki API key from Enoki PortalVITE_GOOGLE_CLIENT_ID: Your Google OAuth Client ID from Google Cloud Console
Smart Contract Configuration
VITE_PACKAGE_ID: The deployed Move contract package ID (obtained after publishing the contract)
Supabase Configuration
VITE_SUPABASE_URL: Your Supabase project URLVITE_SUPABASE_ANON_KEY: Your Supabase anonymous key
VITE_POLL_REGISTRY_ID: PollRegistry object ID (if not set, the app will attempt to find it automatically)
Example .env file:
VITE_ENOKI_API_KEY=your_enoki_api_key_here
VITE_GOOGLE_CLIENT_ID=your_google_client_id_here
VITE_PACKAGE_ID=your_package_id_here
VITE_POLL_REGISTRY_ID=your_poll_registry_id_here
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_supabase_anon_key_here- Go to Google Cloud Console
- Create a new project or select an existing one
- Enable the Google+ API
- Create OAuth 2.0 credentials
- Add authorized JavaScript origins:
http://localhost:5173(for development)- Your production domain (for production)
- Add authorized redirect URIs:
http://localhost:5173/login(for development)https://yourdomain.com/login(for production)
- Copy the Client ID to your
.envfile
- Create a new project at Supabase
- Go to Project Settings > API
- Copy the Project URL and anon/public key
- Add them to your
.envfile - Set up the required database tables (see Database Schema section below)
Navigate to the Move contracts directory:
cd ../moveBuild the contracts:
sui move buildRun tests:
sui move testPublish the contract to Sui testnet:
sui client publish --gas-budget 10000000Copy the Package ID from the output and add it to your .env file as VITE_PACKAGE_ID.
Start the development server:
cd frontend
npm run devThe application will be available at http://localhost:5173.
Build the application:
npm run buildThe production build will be created in the dist directory.
Preview the production build locally:
npm run previewRun unit tests:
npm testRun tests with UI:
npm run test:uiRun tests with coverage:
npm run test:coveragePollar can be deployed to Walrus Sites for decentralized hosting on Sui.
- Install Walrus CLI and site-builder tools
- Configure your Sui wallet for testnet
- Build the frontend application
- Edit
sites-config.yamlin thefrontenddirectory - Ensure your wallet is configured correctly
- Update the
object_idindist/ws-resources.jsonif updating an existing site
cd frontend
npm run build
site-builder --config ./sites-config.yaml --context=testnet deploy ./dist --epochs 1For the first deployment, remove the object_id field from dist/ws-resources.json. The site-builder will create a new site and automatically add the object_id to the file.
To update an existing site, ensure the object_id in dist/ws-resources.json matches your site object ID. The deploy command will update the existing site.
After deployment, you will receive:
- A local development URL (for testnet)
- Instructions for self-hosting a portal
- Information about bringing your own domain
Note: wal.app only supports sites deployed on mainnet. For testnet sites, you need to self-host a portal or use a third-party hosted testnet portal.
Pollar/
├── frontend/ # React frontend application
│ ├── src/
│ │ ├── components/ # React components
│ │ ├── config/ # Configuration files
│ │ ├── constants/ # Application constants
│ │ ├── hooks/ # Custom React hooks
│ │ ├── pages/ # Page components
│ │ ├── styles/ # Global styles
│ │ ├── types/ # TypeScript type definitions
│ │ └── utils/ # Utility functions
│ ├── public/ # Static assets
│ ├── dist/ # Production build output
│ ├── sites-config.yaml # Walrus Sites configuration
│ └── package.json
├── move/ # Sui Move smart contracts
│ ├── sources/ # Move source files
│ ├── tests/ # Move test files
│ └── Move.toml # Move package configuration
└── README.md
The core voting logic is implemented in Sui Move smart contracts located in the move/ directory.
Key Structures:
Poll: Shared object storing poll metadata (title, description, options, start/end times, NFT requirements)VoteRegistry: Dynamic object handling encrypted vote storageUser: Object representing a registered user
Key Functions:
mint_user: Register a new user on-chainmint_poll: Create a new pollvote_sealed: Submit an encrypted vote (public polls)vote_sealed_with_nft: Submit an encrypted vote with NFT ownership validation (NFT-gated polls)
cd move
sui move build
sui move testsui client publish --gas-budget 10000000Pollar uses Supabase (PostgreSQL) for user profiles and vote tracking. The following tables are required:
CREATE TABLE user_profiles (
wallet_address TEXT PRIMARY KEY,
username TEXT,
email TEXT,
created_at TIMESTAMP DEFAULT NOW()
);CREATE TABLE votes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
wallet_address TEXT NOT NULL,
poll_id TEXT NOT NULL,
voted_at TIMESTAMP DEFAULT NOW(),
UNIQUE(wallet_address, poll_id)
);Configuration file for Walrus Sites deployment. Contains:
- Network contexts (testnet/mainnet)
- Package IDs
- Wallet configuration
- Portal settings
Walrus Sites resource configuration. Contains:
- Route mappings for SPA routing
- Site name
- Site object ID (for updates)
- Ensure all environment variables are prefixed with
VITE_ - Restart the development server after changing
.envfile - Check that the
.envfile is in thefrontenddirectory
- Verify redirect URIs match exactly in Google Cloud Console
- Ensure the domain is added to authorized JavaScript origins
- Check that the Client ID is correct in
.env
- Verify the Supabase URL and anon key are correct
- Check that the database tables are created
- Ensure Row Level Security (RLS) policies are configured if needed
- Verify the
VITE_PACKAGE_IDmatches your deployed contract - Ensure the contract is deployed to the correct network (testnet/mainnet)
- Check that the PollRegistry object ID is correct if manually set
- Clear
node_modulesand reinstall:rm -rf node_modules && npm install - Clear Vite cache:
rm -rf node_modules/.vite - Ensure Node.js version is 18 or higher
- Verify wallet configuration in
sites-config.yaml - Check that the
object_idinws-resources.jsonmatches your site (for updates) - Ensure the frontend is built before deploying:
npm run build - Verify you have sufficient SUI tokens for gas fees
- Use TypeScript for all new code
- Follow React best practices and hooks patterns
- Use functional components with hooks
- Maintain consistent naming conventions
- Keep components small and focused
- Extract reusable logic into custom hooks
- Use TypeScript interfaces for props and data structures
- Implement proper error boundaries
- Write unit tests for utility functions
- Test custom hooks in isolation
- Use React Testing Library for component tests
- Maintain test coverage above 70%
This project is open source and available under the MIT License.
For issues, questions, or contributions, please open an issue on the GitHub repository.