feat: Add Community Voting System for Emergency Campaigns#156
feat: Add Community Voting System for Emergency Campaigns#156SUHANI-PAL0103 wants to merge 5 commits intoStabilityNexus:mainfrom
Conversation
- Added geographic location anchoring (Layer 1) - Implemented IPFS document verification (Layer 2) - Integrated 60% community voting threshold (Layer 3) - Designed 40-30-30% staged fund release (Layer 4) - Created comprehensive documentation (README, ARCHITECTURE, CONTRIBUTING) - Enhanced UI with proper contrast and validation - Added Preview Campaign feature for demonstration - Fixed wallet integration with Nautilus - Removed all demo mode code - Added MIT License Technical improvements: - Direct wallet balance fetching via window.ergo API - Token creation modal with real blockchain integration - Form validation with reactive error checking - Emergency document upload (IPFS hash + Drive links) - Staged withdrawal data structures in metadata Documentation: - Complete README with use cases and user guide - ARCHITECTURE.md explaining system design - CONTRIBUTING.md for open source collaboration - Enhanced .gitignore for security
- 5-minute demo script with exact steps - Technical Q&A preparation with honest answers - Innovation highlights for academic presentation - Pre-presentation checklist - Closing statement and resource links - Statistics and key talking points
- Elevator pitch (30 seconds) - 7 core innovations table - Key statistics summary - 5-minute demo flow with timestamps - Top 10 expected Q&A with answers - Pre-demo checklist - Visual aids templates - Social media ready posts - Competition submission template - Lightning round answers (30/10/1 second versions)
- Updated adapter-static configuration for Vercel - Added vercel.json with build settings - Set output directory to build/ - Configured SPA fallback routing
Features: - Community verification voting (60% approval, 20 votes minimum) - Emergency data now extracted from blockchain content - Simplified emergency data structure for box size limits - Donation blocking until verification passes - Free localStorage-based voting (no ERG cost) - CommunityVoting component with progress bars - VotingContract class for vote management - Regional/State community type option - Hide test projects filter disabled by default - BigInt conversion fixes for wallet balance
WalkthroughThis PR rebrands the platform from "Bene" to "Emergency Fundraising Platform on Ergo Blockchain" and introduces a comprehensive 4-layer fraud prevention system. It adds extensive documentation (architecture, voting, enhanced verification, presentation guides), implements community voting via localStorage, introduces staged fund withdrawal with proof requirements, adds direct wallet connectivity, extends project types to include emergency metadata, and updates multiple route components to support emergency campaign flows with verification gating and multi-stage fund release. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/lib/ergo/platform.ts (1)
83-84: Type mismatch:Map<string, number>vsBigIntvalues.The return type declares
Map<string, number>, but the implementation storesBigIntvalues (lines 144, 153). BigInt and number are incompatible types in TypeScript, and this mismatch will cause type errors or unexpected behavior in consuming code.Additionally, the
balancestore (fromstore.ts) is typed aswritable<number | null>but receives abiginton line 145.Apply this diff to fix the type signature:
- async get_balance(id?: string): Promise<Map<string, number>> { - const balanceMap = new Map<string, number>(); + async get_balance(id?: string): Promise<Map<string, bigint>> { + const balanceMap = new Map<string, bigint>();Also update
src/lib/common/store.ts:-export const balance = writable<number | null>(null); +export const balance = writable<bigint | null>(null);src/routes/NewProject.svelte (1)
628-643: Emergency data is intentionally simplified for blockchain submission, but critical verification fields are being lost.The form collects
emergencyLocation,hospitalAuthorityName,documentTypes, anddocumentDescription, along with IPFS hashes inemergencyDocuments, but onlytype,community,phase, andvotesare included in the on-chain payload (lines 628-640). While the comment acknowledges blockchain box size constraints, the omitted fields are necessary for the community voting workflow—CommunityVoting.sveltedisplaysdocumentDescriptionanddocumentHashes, but these are never transmitted. Either:
- Store these fields off-chain (e.g., IPFS with hash on-chain), similar to how documents are handled
- Remove the form inputs if they're not intended for use
- Document clearly that location anchoring and document types are verified client-side only
🧹 Nitpick comments (23)
.gitignore (2)
18-23: Remove duplicate.DS_Storeentry.The
.DS_Storepattern appears in both the IDE section (line 18) and OS section (line 34). Consolidate to a single location to improve maintainability.Apply this diff to remove the duplicate:
# IDE .DS_Store .idea/ .vscode/ *.swp *.swo *~ # Logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* # OS -Thumbs.db -.DS_Store +Thumbs.dbAlso applies to: 32-35
55-56: Clarify the purpose of.service/directory.The
.service/directory entry is unclear without context. Add a comment explaining what this directory contains (e.g., mock services, local service files) to help maintainers understand why it should be ignored.For example:
# Local Development +# Local service mocks and data .service/ local-data/src/lib/ergo/platform.ts (1)
101-116: Consider adding a type for wallet balance response.Using
anytype (line 102) reduces type safety. Consider defining an interface for the expected wallet balance formats to improve maintainability and catch issues at compile time.interface WalletBalanceResponse { nanoErgs?: bigint | string | number; balance?: string; tokens?: Array<{ tokenId: string; amount: string | bigint }>; }src/routes/App.svelte (1)
347-352: Alternative wallet connection block appears intended for debugging.This always-visible banner with hardcoded light-mode colors won't adapt to the app's dark theme (note the existing
mode-watcherintegration). The message "If the main wallet button isn't working" suggests this is a fallback for development/debugging purposes.Consider:
- Making this conditional (e.g., only show when wallet connection fails or via a dev flag)
- Using theme-aware styles consistent with the rest of the app
- Removing before production if the main wallet button is stable
- <!-- Direct Wallet Connect (Alternative Method) --> - <div style="background: #f0f9ff; border: 2px solid #3b82f6; padding: 1rem; margin: 1rem auto; max-width: 600px; border-radius: 0.5rem; text-align: center;"> - <h3 style="margin: 0 0 0.5rem 0; color: #1e40af;">Alternative Wallet Connection</h3> - <p style="margin: 0 0 1rem 0; font-size: 0.875rem; color: #475569;">If the main wallet button isn't working, try this direct connection method:</p> - <DirectWalletConnect /> - </div> + <!-- Direct Wallet Connect - Remove or conditionally render before production --> + {#if !$walletConnected} + <div class="alternative-wallet-banner"> + <h3>Alternative Wallet Connection</h3> + <p>If the main wallet button isn't working, try this direct connection method:</p> + <DirectWalletConnect /> + </div> + {/if}Then add theme-aware styles in the
<style>section.src/lib/common/project.ts (1)
55-62: ProjectContent interface duplication is intentional but could be documented better.Two separate
ProjectContentinterfaces exist:
src/lib/ergo/utils/box-size-calculator.ts(lines 30-35): Contains core fields for validation (title, description, image, link)src/lib/common/project.ts(lines 55-62): Contains full project data including raw JSON and emergency fieldThe duplication is intentional—
submit.tsuses the lighter box-size-calculator version for validation since only the core four fields are needed for size calculations. The full interface with theemergencyfield is used elsewhere viagetProjectContent()infetch.ts. However, this design could be clearer: consider adding a comment explaining why two interfaces exist, or unify them with optional fields to reduce confusion.src/lib/components/DirectWalletConnect.svelte (2)
96-99: Potential runtime error whenwalletAddressis empty.If
connectedbecomestruebutwalletAddressis somehow an empty string, thesubstringcalls at line 98 will produce incorrect output (e.g.,...with no address prefix). While this is unlikely given the current flow, consider adding a guard:{:else} <div class="connected-info"> <span class="success-icon">✅</span> - <span class="address">{walletAddress.substring(0, 8)}...{walletAddress.substring(walletAddress.length - 6)}</span> + <span class="address"> + {walletAddress.length > 14 + ? `${walletAddress.substring(0, 8)}...${walletAddress.substring(walletAddress.length - 6)}` + : walletAddress} + </span> </div> {/if}
35-54: Consider adding a timeout for wallet connection.The
nautilus.connect()and subsequent API calls could hang indefinitely if the wallet extension is unresponsive or the user doesn't interact with the popup. Consider wrapping these in a timeout:// Helper to add timeout to promises const withTimeout = <T>(promise: Promise<T>, ms: number): Promise<T> => { return Promise.race([ promise, new Promise<T>((_, reject) => setTimeout(() => reject(new Error('Connection timed out')), ms) ) ]); }; // Usage: const nautilusApi = await withTimeout( (window as any).ergoConnector.nautilus.connect(), 30000 // 30 second timeout );src/routes/ProjectDetails.svelte (1)
599-652: Vote handling updates local state without persisting to blockchain.The
handleVerificationVotefunction updates the localprojectobject and Svelte store, but these changes are not persisted to the blockchain. When the user refreshes the page, the voting state will be lost (only localStorage via VotingContract persists). This is documented as demo behavior, but ensure users understand this limitation.Additionally, the error handling shows the error both in
errorMessage(line 647) and viaalert()(line 648), which is redundant:} catch (error) { console.error('Voting error:', error); errorMessage = error instanceof Error ? error.message : 'Failed to submit vote. Please try again.'; - alert(errorMessage); + // errorMessage is displayed in the UI, no need for alert } finally {VOTING_SYSTEM_IMPLEMENTATION.md (1)
238-247: Add language specifier to fenced code blocks for proper syntax highlighting.Per static analysis hints, several code blocks lack language specifiers which affects rendering and syntax highlighting:
### Phase Transitions: -``` +```text PENDING_VERIFICATION (initial) ↓ (votes < 20) UNDER_REVIEW (collecting votes)Similarly for lines 254 and 267. This is a minor formatting improvement.
src/routes/StagedWithdrawal.svelte (1)
56-83: Direct mutation of prop objects won't trigger parent reactivity.Directly mutating
stage.statusandstage.withdrawnAt(lines 71-72) modifies the prop array objects in place. While this works, it's a Svelte anti-pattern that can lead to reactivity issues - the parent component won't be notified of changes.Consider using event dispatching or callbacks:
+import { createEventDispatcher } from "svelte"; + +const dispatch = createEventDispatcher(); + async function requestWithdrawal(stage: WithdrawalStage) { // ... try { // ... - stage.status = 'withdrawn'; - stage.withdrawnAt = Date.now(); + dispatch('stageWithdrawn', { + stageNumber: stage.stageNumber, + status: 'withdrawn', + withdrawnAt: Date.now() + }); // ... } }Then the parent can handle the event and update state properly.
README.md (3)
127-131: Add language specifier to code block.Per static analysis, this code block should have a language specifier:
4. **Open Browser** -``` +```text http://localhost:5173--- `587-590`: **Fix empty link and bare URL.** Static analysis flagged formatting issues: ```diff - **GitHub Issues:** [Report bugs or request features](https://github.com/YourUsername/emergency-fundraising-ergo/issues) -- **Discord:** [Join our community](#) *(coming soon)* -- **Email:** support@emergency-fundraising.io *(coming soon)* +- **Discord:** Join our community *(coming soon)* +- **Email:** `support@emergency-fundraising.io` *(coming soon)*Empty links (
#) and bare URLs should be avoided per markdown best practices.
516-521: Clarify Phase 2 completion status.Lines 516-520 show checkmarks (✅) for Phase 2 items, but line 520-521 indicate ErgoScript implementation is still pending (🔄). Consider updating the checkmarks to reflect that these are "frontend ready" rather than fully complete:
**Phase 2 (Next 3 months):** Smart Contract Enforcement -- ✅ Implement voting logic UI (frontend ready) -- ✅ Community voting interface with progress tracking -- ✅ Automated stage transition UI -- ✅ Document hash verification interface +- ✅ Implement voting logic UI *(frontend complete)* +- ✅ Community voting interface with progress tracking *(frontend complete)* +- ✅ Automated stage transition UI *(frontend complete)* +- ✅ Document hash verification interface *(frontend complete)* - 🔄 ErgoScript implementation (backend logic pending) - 🔄 Oracle integration for off-chain dataThis clarifies that the backend/blockchain integration is still pending.
src/routes/CommunityVoting.svelte (4)
29-29: Potential stale state on prop changes.
votingStateis initialized once at module top-level fromVotingContract.getVotingState(campaignId). IfcampaignIdprop changes, this value won't update automatically.Consider making this reactive or reloading in
onMountwhen the prop changes:- let votingState = VotingContract.getVotingState(campaignId); + let votingState: ReturnType<typeof VotingContract.getVotingState>; + + $: { + votingState = VotingContract.getVotingState(campaignId); + }
46-57: Mutating theemergencyDataprop directly may cause unexpected behavior.Svelte props are typically treated as read-only. Mutating
emergencyData.votesdirectly can lead to subtle bugs, especially if the parent component doesn't expect this mutation or if reactivity isn't properly triggered.Consider using a callback prop or dispatching an event to notify the parent of vote state changes instead of direct mutation:
+ import { createEventDispatcher } from "svelte"; + const dispatch = createEventDispatcher(); + function loadVotingState() { votingState = VotingContract.getVotingState(campaignId); - - // Update emergency data with real voting state - if (emergencyData.votes) { - emergencyData.votes.approved = votingState.approved; - emergencyData.votes.rejected = votingState.rejected; - emergencyData.votes.total = votingState.total; - emergencyData.votes.voters = votingState.voters; - } else { - emergencyData.votes = votingState; - } + dispatch('votingStateChanged', votingState); }
60-67: Race condition:checkIfUserVoteddepends on$addressbut may run before address is available.The function is called in
onMount, but$addressmay still be null/undefined at that point if the wallet connection is asynchronous.Add a reactive statement to re-check when address becomes available:
+ $: if ($address) { + checkIfUserVoted(); + } + function checkIfUserVoted() { if (!$address) return; hasVoted = VotingContract.hasUserVoted(campaignId, $address); if (hasVoted) { userVote = VotingContract.getUserVote(campaignId, $address); } }
19-19: Unused import:ErgoPlatformis instantiated but never used.The
platformvariable is created but not referenced anywhere in the component.- import { ErgoPlatform } from "$lib/ergo/platform"; ... - let platform = new ErgoPlatform();src/routes/NewProject.svelte (3)
2563-2563: Alert used for success/error messaging is a poor UX pattern.Using
alert()for transaction results blocks the UI and doesn't allow users to copy the transaction ID easily. Consider using a toast notification or inline message instead.Consider using a toast/snackbar component or updating the modal to show success/error states inline.
380-386: Balance validation condition may have false negatives.The condition
maxTokenAmountToSell > 0could incorrectly skip validation when the user has a zero balance but has selected a token and entered an amount.- if (rewardTokenId && tokenAmountToSellPrecise > maxTokenAmountToSell && maxTokenAmountToSell > 0) { + if (rewardTokenId && tokenAmountToSellPrecise > 0 && tokenAmountToSellPrecise > maxTokenAmountToSell) { formErrors.amountExceedsBalance = "Amount exceeds available balance."; } else { formErrors.amountExceedsBalance = null; }
119-120: Magic numbers for voting parameters should be constants or configurable.
minimumCommunityMembers: number = 20andregionalVotingPercentage: number = 60are hardcoded but should ideally reference the same constants used inVotingContractto ensure consistency.Consider importing these from a shared constants file or from VotingContract:
import { VotingContract } from "$lib/ergo/voting/voting_contract"; // Then use VotingContract.MIN_VOTES_REQUIRED and VotingContract.VOTING_THRESHOLDNote: This would require making those constants public in VotingContract.
src/lib/ergo/voting/voting_contract.ts (3)
6-6: Unused imports from @fleet-sdk/core.
ErgoAddress,OutputBuilder,TransactionBuilder, andRECOMMENDED_MIN_FEE_VALUEare imported but never used in this file. These appear to be placeholders for future on-chain implementation.Remove unused imports to keep the code clean:
-import { ErgoAddress, OutputBuilder, TransactionBuilder, RECOMMENDED_MIN_FEE_VALUE } from "@fleet-sdk/core"; +// TODO: Import Fleet SDK when implementing on-chain voting +// import { ErgoAddress, OutputBuilder, TransactionBuilder, RECOMMENDED_MIN_FEE_VALUE } from "@fleet-sdk/core";
107-120:getUserVoteparses storage redundantly.This method parses the same localStorage data that
getVotesFromStoragehandles. Consider refactoring to reuse the existing method.static getUserVote(campaignId: string, walletAddress: string): 'approved' | 'rejected' | null { - const storageKey = `${this.VOTE_STORAGE_KEY}${campaignId}`; - const data = localStorage.getItem(storageKey); - - if (!data) return null; - - try { - const votes: VoteData[] = JSON.parse(data); - const userVote = votes.find(v => v.voter === walletAddress); - return userVote ? userVote.voteType : null; - } catch { - return null; - } + const votes = this.exportVotesForBlockchain(campaignId); + const userVote = votes.find(v => v.voter === walletAddress); + return userVote ? userVote.voteType : null; }
33-87:submitVoteacceptsprojectparameter but only uses it for phase validation.The
projectparameter is passed but underutilized. Consider if the full Project object is needed or if just the phase could be passed.For cleaner API design:
static async submitVote( campaignId: string, - project: Project, + currentPhase: CampaignPhase, voteType: 'approved' | 'rejected', walletAddress: string ): Promise<{ success: boolean; message: string; votingState: VotingState }> { ... - if (!project.content.emergency?.phase || - (project.content.emergency.phase !== CampaignPhase.PENDING_VERIFICATION && - project.content.emergency.phase !== CampaignPhase.UNDER_REVIEW)) { + if (currentPhase !== CampaignPhase.PENDING_VERIFICATION && + currentPhase !== CampaignPhase.UNDER_REVIEW) { throw new Error("Campaign is not in verification phase"); }This reduces coupling and makes the API clearer about what data it actually needs.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (23)
.gitignore(1 hunks)ARCHITECTURE.md(1 hunks)CONTRIBUTING.md(1 hunks)ENHANCED_VERIFICATION_MODEL.md(1 hunks)LICENSE(1 hunks)PRESENTATION_GUIDE.md(1 hunks)QUICK_REFERENCE.md(1 hunks)README.md(1 hunks)VOTING_SYSTEM_IMPLEMENTATION.md(1 hunks)src/lib/common/project.ts(2 hunks)src/lib/components/DirectWalletConnect.svelte(1 hunks)src/lib/ergo/platform.ts(1 hunks)src/lib/ergo/voting/voting_contract.ts(1 hunks)src/routes/App.svelte(2 hunks)src/routes/CommunityVoting.svelte(1 hunks)src/routes/NewProject.svelte(18 hunks)src/routes/Phase2Demo.svelte(1 hunks)src/routes/ProjectCard.svelte(5 hunks)src/routes/ProjectDetails.svelte(7 hunks)src/routes/ProjectList.svelte(1 hunks)src/routes/StagedWithdrawal.svelte(1 hunks)svelte.config.js(1 hunks)vercel.json(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/lib/ergo/voting/voting_contract.ts (1)
src/lib/common/project.ts (1)
Project(104-129)
src/lib/ergo/platform.ts (1)
src/lib/common/store.ts (1)
balance(9-9)
src/lib/common/project.ts (1)
src/lib/ergo/utils/box-size-calculator.ts (1)
ProjectContent(30-35)
🪛 LanguageTool
ARCHITECTURE.md
[style] ~231-~231: The double modal “Requires verified” is nonstandard (only accepted in certain dialects). Consider “to be verified”.
Context: ...ts Voting: -
(NEEDS_FIXED)
CONTRIBUTING.md
[style] ~456-~456: ‘Emergency situations’ might be wordy. Consider a shorter alternative.
Context: ... ### Design Principles 1. Clarity: Emergency situations need clear, simple interfaces 2. **Trus...
(EN_WORDINESS_PREMIUM_EMERGENCY_SITUATIONS)
QUICK_REFERENCE.md
[style] ~208-~208: This phrasing can be overused. Try elevating your writing with a more formal alternative.
Context: ...so communities can fork and remove fees if they want. Sustainability comes from scale, not h...
(IF_YOU_WANT)
PRESENTATION_GUIDE.md
[style] ~142-~142: For conciseness, consider replacing this expression with an adverb.
Context: ...n-chain for smart contract enforcement. Right now, the innovation is in the architectural...
(AT_THE_MOMENT)
[uncategorized] ~316-~316: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...* is acceptable - Offer to research and follow up - Redirect to what you DO know - Stay c...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
[style] ~408-~408: ‘future plans’ might be wordy. Consider a shorter alternative.
Context: ...proud, be honest about current state vs future plans, and focus on the innovation of the DES...
(EN_WORDINESS_PREMIUM_FUTURE_PLANS)
🪛 markdownlint-cli2 (0.18.1)
ARCHITECTURE.md
5-5: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
28-28: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
72-72: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
79-79: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
85-85: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
95-95: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
150-150: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
161-161: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
175-175: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
188-188: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
195-195: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
203-203: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
292-292: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
317-317: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
323-323: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
ENHANCED_VERIFICATION_MODEL.md
65-65: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
66-66: Unordered list indentation
Expected: 0; Actual: 2
(MD007, ul-indent)
191-191: Bare URL used
(MD034, no-bare-urls)
201-201: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
217-217: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
CONTRIBUTING.md
9-9: Bare URL used
(MD034, no-bare-urls)
53-53: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
59-59: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
82-82: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
89-89: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
252-252: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
257-257: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
262-262: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
365-365: Bare URL used
(MD034, no-bare-urls)
368-368: Bare URL used
(MD034, no-bare-urls)
410-410: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
README.md
128-128: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
282-282: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
324-324: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
331-331: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
588-588: No empty links
(MD042, no-empty-links)
589-589: Bare URL used
(MD034, no-bare-urls)
621-621: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
623-623: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
QUICK_REFERENCE.md
6-6: Bare URL used
(MD034, no-bare-urls)
143-143: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
144-144: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
150-150: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
151-151: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
158-158: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
159-159: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
166-166: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
167-167: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
173-173: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
174-174: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
198-198: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
201-201: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
204-204: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
207-207: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
215-215: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
224-224: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
240-240: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
301-301: Bare URL used
(MD034, no-bare-urls)
321-321: No space after hash on atx style heading
(MD018, no-missing-space-atx)
325-325: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
VOTING_SYSTEM_IMPLEMENTATION.md
238-238: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
254-254: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
267-267: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
399-399: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
🔇 Additional comments (19)
.gitignore (1)
49-52: Good security practice for wallet and seed file protection.The wallet and seed ignores with the "NEVER COMMIT!" warning are appropriate. The codebase integrates with external wallet libraries (wallet-svelte-component) and does not generate
.key,.pem, or*.privateKeyfiles locally, so the current patterns (*.wallet,*.seed,mnemonic.txt) are sufficient for this project. If you extend wallet functionality to generate additional key file formats in the future, add corresponding patterns to.gitignoreat that time.LICENSE (1)
1-21: LGTM!Standard MIT License with appropriate copyright holder. The license text is complete and correctly formatted.
vercel.json (1)
1-11: Configuration looks correct for static SPA deployment.The settings align with
svelte.config.js(output tobuild,index.htmlfallback). Note that Vercel typically auto-detects SvelteKit projects, so this explicit configuration may be redundant unless you're overriding specific defaults.QUICK_REFERENCE.md (2)
5-9: Verify the GitHub repository URL is correct.Line 6 points to
SUHANI-PAL0103/BenefactionPlatform-Ergo, but the PR is targetingStabilityNexus/BenefactionPlatform-Ergo. If this documentation is for the main project, consider updating to the organization repository URL for consistency.
1-325: Well-structured quick reference documentation.The content provides comprehensive presentation guidance including demo flows, Q&A preparation, and checklists. The markdown formatting warnings from static analysis (bare URLs, missing code block languages) are minor stylistic issues that don't affect usability.
CONTRIBUTING.md (2)
481-506: Verify contact information before publishing.The security and support contact details contain placeholder or potentially non-existent addresses:
security@emergency-fundraising.io(Line 486)dev@emergency-fundraising.io(Line 505)- Discord link is just
#(Line 504)Ensure these channels exist and are monitored, or update to valid contact methods.
1-524: Comprehensive contributing guidelines.The document covers prerequisites, development workflow, code style, testing, PR process, and security reporting effectively. The structure follows industry best practices for open-source contribution guides.
svelte.config.js (1)
14-21: LGTM!The adapter configuration correctly sets up a static SPA build with
index.htmlfallback for client-side routing. Thestrict: trueoption will catch any prerendering issues during build. Settings align withvercel.json.ARCHITECTURE.md (1)
1-356: Excellent architecture documentation.Comprehensive coverage of system components, data flows, security considerations, and implementation status. The honest status indicators (✅/
⚠️ /❌) clearly communicate what's implemented vs. planned, which is valuable for contributors and reviewers.ENHANCED_VERIFICATION_MODEL.md (1)
1-217: Well-documented verification model.The 4-step verification flow is clearly explained with academic justifications and practical implementation status. The talking points section is particularly useful for presentations. The privacy considerations (approximate location vs. GPS, hashes vs. raw files) demonstrate thoughtful design.
src/lib/ergo/platform.ts (1)
118-159: Robust multi-format balance handling.The logic correctly handles various wallet balance response formats (nanoErgs object, balance string, direct bigint). The token normalization to BigInt ensures consistent handling across different wallet implementations.
src/routes/ProjectList.svelte (1)
24-24: Verify intentional default change for production.Changing
hideTestProjectsfromtruetofalsemeans test campaigns will now be visible by default to all users. While this is helpful during development and testing of the voting system, consider whether this should be reverted before merging to production, or if there's a reason to keep test campaigns visible to end users.src/lib/common/project.ts (2)
12-53: Well-structured types for the voting and withdrawal system.The
CampaignPhaseenum,WithdrawalStage, andEmergencyDatainterfaces are well-designed for the fraud prevention system. The voter tracking arrays enable double-vote prevention as intended.
159-161: LGTM - Emergency data extraction.The extraction logic correctly handles the optional
emergencyfield, falling back toundefinedwhen not present.src/routes/Phase2Demo.svelte (1)
1-84: Well-organized demo component.The demo data structures are comprehensive and correctly typed against the
EmergencyDataandWithdrawalStageinterfaces. The toggle between voting and withdrawal modes provides a clear demonstration of Phase 2 features.src/routes/ProjectCard.svelte (3)
33-36: BigInt to Number conversion is safe for typical balances.The conversion
Number($balance)works correctly for ERG balances since they won't exceedNumber.MAX_SAFE_INTEGER(~9 quadrillion nanoERGs, far beyond circulating supply). Good fix for the BigInt display issue.
120-158: Emergency status displayed in two places.The emergency phase is shown both in the header (lines 120-158) as status badges and in the content area (lines 258-272) as a donation-blocked alert. For
PENDING_VERIFICATION,UNDER_REVIEW, andREJECTEDphases, users see both indicators simultaneously.If this is intentional (header shows campaign phase, content emphasizes donation blocking), the current approach is fine. If not, consider consolidating to reduce visual redundancy.
Also applies to: 258-272
346-381: Well-structured CSS for donation status alerts.The new
.donation-status-alertstyles with.pendingand.rejectedvariants are clean and consistent with the existing badge styling patterns.src/lib/ergo/voting/voting_contract.ts (1)
17-23:VotingState.votersstores wallet addresses which could be PII.Storing wallet addresses in localStorage as an array of voters could have privacy implications. While wallet addresses are pseudo-anonymous, the pattern of storing them locally and potentially exporting them should be considered.
Verify if storing voter addresses locally aligns with the project's privacy requirements and if users are informed about this data collection.
| ### Reporting Bugs | ||
|
|
||
| **Before submitting a bug report:** | ||
| - Check if the bug has already been reported in [Issues](https://github.com/YourUsername/emergency-fundraising-ergo/issues) |
There was a problem hiding this comment.
Update placeholder repository URL.
The Issues link contains YourUsername placeholder. Update to the actual repository URL (e.g., StabilityNexus/BenefactionPlatform-Ergo).
🤖 Prompt for AI Agents
In CONTRIBUTING.md around line 43, the Issues link uses the placeholder
`YourUsername`; update that markdown link to point to the real repository (for
example replace
`https://github.com/YourUsername/emergency-fundraising-ergo/issues` with
`https://github.com/StabilityNexus/BenefactionPlatform-Ergo/issues`) so the link
resolves correctly to the project's Issues page.
| ## 🚀 Next Steps | ||
|
|
||
| 1. **Test the new form fields** at http://localhost:5174/ | ||
| 2. **Navigate to "New Campaign"** tab | ||
| 3. **Fill out all enhanced fields** | ||
| 4. **Show your teacher** the structured verification approach | ||
| 5. **Explain the anti-corruption logic** using this document | ||
|
|
There was a problem hiding this comment.
Port number inconsistency.
Line 191 references localhost:5174, but other documentation (QUICK_REFERENCE.md) references localhost:5173. Verify which port is correct and update for consistency.
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)
191-191: Bare URL used
(MD034, no-bare-urls)
🤖 Prompt for AI Agents
In ENHANCED_VERIFICATION_MODEL.md around lines 189 to 196, the doc references
http://localhost:5174 which is inconsistent with QUICK_REFERENCE.md's
localhost:5173; verify the correct dev server port (check package.json scripts,
Vite/DevServer config, or QUICK_REFERENCE.md source) and update this file to use
the confirmed port so both docs match.
| **Student:** Suhani Pal | ||
| **Institution:** [Your Institution] | ||
| **Project Type:** Hackathon/Thesis Project | ||
| **Date:** December 2025 | ||
| **GitHub:** https://github.com/SUHANI-PAL0103/BenefactionPlatform-Ergo |
There was a problem hiding this comment.
Fill in placeholder values before merging.
The document contains several placeholders that should be completed:
- Line 6:
[Your Institution] - Line 9: GitHub URL points to a personal fork rather than the main repository
**Student:** Suhani Pal
-**Institution:** [Your Institution]
+**Institution:** <actual institution name>
**Project Type:** Hackathon/Thesis Project
**Date:** December 2025
-**GitHub:** https://github.com/SUHANI-PAL0103/BenefactionPlatform-Ergo
+**GitHub:** https://github.com/StabilityNexus/BenefactionPlatform-Ergo📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| **Student:** Suhani Pal | |
| **Institution:** [Your Institution] | |
| **Project Type:** Hackathon/Thesis Project | |
| **Date:** December 2025 | |
| **GitHub:** https://github.com/SUHANI-PAL0103/BenefactionPlatform-Ergo | |
| **Student:** Suhani Pal | |
| **Institution:** <actual institution name> | |
| **Project Type:** Hackathon/Thesis Project | |
| **Date:** December 2025 | |
| **GitHub:** https://github.com/StabilityNexus/BenefactionPlatform-Ergo |
🤖 Prompt for AI Agents
In PRESENTATION_GUIDE.md around lines 5 to 9, replace the placeholder “[Your
Institution]” with the actual institution name on line 6 and update the GitHub
URL on line 9 to point to the canonical/main repository (not a personal fork);
also scan the file for any other placeholder values (names, dates, links) and
update them to final, accurate values before merging.
|
|
||
| **Contact:** | ||
| - GitHub: @SUHANI-PAL0103 | ||
| - Email: [Your Email] | ||
| - LinkedIn: [Your Profile] | ||
|
|
There was a problem hiding this comment.
Additional placeholders need completion.
**Contact:**
- GitHub: @SUHANI-PAL0103
-- Email: [Your Email]
-- LinkedIn: [Your Profile]
+- Email: <actual email>
+- LinkedIn: <actual profile URL>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| **Contact:** | |
| - GitHub: @SUHANI-PAL0103 | |
| - Email: [Your Email] | |
| - LinkedIn: [Your Profile] | |
| **Contact:** | |
| - GitHub: @SUHANI-PAL0103 | |
| - Email: <actual email> | |
| - LinkedIn: <actual profile URL> |
🤖 Prompt for AI Agents
In PRESENTATION_GUIDE.md around lines 374 to 379, the contact section contains
placeholder values; replace [Your Email] and [Your Profile] with your actual
professional email address and LinkedIn URL, and ensure the GitHub handle is
correct (e.g., @SUHANI-PAL0103) or update it to the real handle; keep the format
consistent (plain text or markdown links), verify links work, and avoid
including any personal information you don't want published.
| // Check if definitively rejected (even if more votes come, can't reach threshold) | ||
| const remainingVotes = 100 - votingState.total; // Hypothetical max votes | ||
| const maxPossibleApproval = ((votingState.approved + remainingVotes) / 100) * 100; | ||
|
|
||
| if (maxPossibleApproval < this.VOTING_THRESHOLD) { | ||
| return CampaignPhase.REJECTED; | ||
| } |
There was a problem hiding this comment.
Rejection calculation logic is flawed.
The remainingVotes = 100 - votingState.total assumes a maximum of 100 total votes, which is an arbitrary limit not enforced elsewhere. This could cause campaigns to be incorrectly marked as REJECTED.
The logic for determining if a campaign is "definitively rejected" needs reconsideration. With unlimited voters, a campaign can theoretically always recover:
- // Check if definitively rejected (even if more votes come, can't reach threshold)
- const remainingVotes = 100 - votingState.total; // Hypothetical max votes
- const maxPossibleApproval = ((votingState.approved + remainingVotes) / 100) * 100;
-
- if (maxPossibleApproval < this.VOTING_THRESHOLD) {
- return CampaignPhase.REJECTED;
- }
+ // Only reject if enough votes have been cast AND approval is below threshold
+ // Consider adding a maximum voting period or vote cap for definitive rejection
+ if (votingState.total >= this.MIN_VOTES_REQUIRED) {
+ const approvalPercentage = (votingState.approved / votingState.total) * 100;
+ if (approvalPercentage < (100 - this.VOTING_THRESHOLD)) {
+ // If rejection percentage exceeds what's needed to block approval
+ return CampaignPhase.REJECTED;
+ }
+ }Alternatively, document the intended behavior if 100 is the intended cap.
🤖 Prompt for AI Agents
In src/lib/ergo/voting/voting_contract.ts around lines 146-152, the rejection
check uses a hardcoded 100-vote cap (remainingVotes = 100 - votingState.total)
which is incorrect; replace that with the real source of maximum possible votes
or remove the definitive-reject branch: either (A) if the system enforces a max
voters cap, read that cap from the campaign config (e.g. campaign.maxVoters or a
constant) and compute remainingVotes = maxVoters - votingState.total and then
calculate maxPossibleApproval from that; or (B) if there is no cap, remove the
“definitively rejected” logic and only determine REJECTED when the voting period
is closed or when quorum/threshold rules based on actual votes are met;
implement the chosen approach consistently and add a comment documenting the
expectation (cap vs. unlimited voters).
| const totalVotes = (stage.votesFor || 0) + (stage.votesAgainst || 0); | ||
| const approvalRate = (stage.votesFor || 0) / totalVotes * 100; | ||
|
|
||
| // Check if enough votes to finalize (minimum 5 votes) | ||
| if (totalVotes >= 5) { | ||
| if (approvalRate >= 60) { | ||
| stage.status = 'approved'; | ||
| stage.approvedAt = Date.now(); | ||
| // Set timelock based on stage | ||
| const delays = [0, 7 * 24 * 60 * 60 * 1000, 14 * 24 * 60 * 60 * 1000]; // 0, 7, 14 days in ms | ||
| stage.timelock = Date.now() + delays[stageNumber - 1]; | ||
| alert(`✅ Stage ${stageNumber} APPROVED!\nTime lock: ${delays[stageNumber - 1] / (24 * 60 * 60 * 1000)} days`); | ||
| } else if (approvalRate < 40) { | ||
| stage.status = 'rejected'; | ||
| alert(`❌ Stage ${stageNumber} REJECTED by community.`); | ||
| } | ||
| } | ||
|
|
||
| // Update project | ||
| project = { ...project }; | ||
| project_detail.set(project); | ||
|
|
||
| alert(`Vote recorded: ${voteType === 'approve' ? '✓ Approved' : '✕ Rejected'}`); |
There was a problem hiding this comment.
Potential division by zero and multiple alert() calls.
- Division by zero: If both
votesForandvotesAgainstare 0 (shouldn't happen after vote, but defensive coding helps):
- const approvalRate = (stage.votesFor || 0) / totalVotes * 100;
+ const approvalRate = totalVotes > 0 ? ((stage.votesFor || 0) / totalVotes) * 100 : 0;- Multiple alerts: Lines 717, 720 show status-specific alerts, then line 728 shows another "Vote recorded" alert. The user sees two alerts in succession:
if (totalVotes >= 5) {
if (approvalRate >= 60) {
stage.status = 'approved';
stage.approvedAt = Date.now();
const delays = [0, 7 * 24 * 60 * 60 * 1000, 14 * 24 * 60 * 60 * 1000];
stage.timelock = Date.now() + delays[stageNumber - 1];
alert(`✅ Stage ${stageNumber} APPROVED!\nTime lock: ${delays[stageNumber - 1] / (24 * 60 * 60 * 1000)} days`);
+ return; // Skip generic alert
} else if (approvalRate < 40) {
stage.status = 'rejected';
alert(`❌ Stage ${stageNumber} REJECTED by community.`);
+ return; // Skip generic alert
}
}
// ... project update code ...
- alert(`Vote recorded: ${voteType === 'approve' ? '✓ Approved' : '✕ Rejected'}`);
+ // Only show generic alert if no status change alert was shown
+ if (stage.status === 'pending') {
+ alert(`Vote recorded: ${voteType === 'approve' ? '✓ Approved' : '✕ Rejected'}`);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const totalVotes = (stage.votesFor || 0) + (stage.votesAgainst || 0); | |
| const approvalRate = (stage.votesFor || 0) / totalVotes * 100; | |
| // Check if enough votes to finalize (minimum 5 votes) | |
| if (totalVotes >= 5) { | |
| if (approvalRate >= 60) { | |
| stage.status = 'approved'; | |
| stage.approvedAt = Date.now(); | |
| // Set timelock based on stage | |
| const delays = [0, 7 * 24 * 60 * 60 * 1000, 14 * 24 * 60 * 60 * 1000]; // 0, 7, 14 days in ms | |
| stage.timelock = Date.now() + delays[stageNumber - 1]; | |
| alert(`✅ Stage ${stageNumber} APPROVED!\nTime lock: ${delays[stageNumber - 1] / (24 * 60 * 60 * 1000)} days`); | |
| } else if (approvalRate < 40) { | |
| stage.status = 'rejected'; | |
| alert(`❌ Stage ${stageNumber} REJECTED by community.`); | |
| } | |
| } | |
| // Update project | |
| project = { ...project }; | |
| project_detail.set(project); | |
| alert(`Vote recorded: ${voteType === 'approve' ? '✓ Approved' : '✕ Rejected'}`); | |
| const totalVotes = (stage.votesFor || 0) + (stage.votesAgainst || 0); | |
| const approvalRate = totalVotes > 0 ? ((stage.votesFor || 0) / totalVotes) * 100 : 0; | |
| // Check if enough votes to finalize (minimum 5 votes) | |
| if (totalVotes >= 5) { | |
| if (approvalRate >= 60) { | |
| stage.status = 'approved'; | |
| stage.approvedAt = Date.now(); | |
| // Set timelock based on stage | |
| const delays = [0, 7 * 24 * 60 * 60 * 1000, 14 * 24 * 60 * 60 * 1000]; // 0, 7, 14 days in ms | |
| stage.timelock = Date.now() + delays[stageNumber - 1]; | |
| alert(`✅ Stage ${stageNumber} APPROVED!\nTime lock: ${delays[stageNumber - 1] / (24 * 60 * 60 * 1000)} days`); | |
| } else if (approvalRate < 40) { | |
| stage.status = 'rejected'; | |
| alert(`❌ Stage ${stageNumber} REJECTED by community.`); | |
| } | |
| } | |
| // Update project | |
| project = { ...project }; | |
| project_detail.set(project); | |
| alert(`Vote recorded: ${voteType === 'approve' ? '✓ Approved' : '✕ Rejected'}`); |
🤖 Prompt for AI Agents
In src/routes/ProjectDetails.svelte around lines 706 to 728, calculate
totalVotes and guard against division by zero by only computing approvalRate
when totalVotes > 0 (otherwise set approvalRate = 0 or skip approval checks),
then only show one user alert: either the status-specific alert
(approved/rejected) or the generic "Vote recorded" message but not both —
implement logic to suppress the generic alert when a status alert was already
shown (e.g., track a boolean like statusAlertShown), and keep timelock
assignment as-is; ensure approvalRate uses the guarded value so no NaN/Infinity
occurs.
| // Move to next stage | ||
| const nextStage = project.content.emergency.withdrawalStages[stageNumber]; | ||
| if (nextStage && nextStage.status === 'pending') { | ||
| project.content.emergency.currentStage = stageNumber + 1; | ||
| } |
There was a problem hiding this comment.
Potential off-by-one error in stage progression logic.
The stageNumber is 1-based, so accessing withdrawalStages[stageNumber] after withdrawing stage 1 would access index 1 (stage 2), which is correct. However, the condition nextStage.status === 'pending' may fail since stages start as 'pending' but this logic only triggers when a stage is withdrawn. Consider:
// Move to next stage
- const nextStage = project.content.emergency.withdrawalStages[stageNumber];
- if (nextStage && nextStage.status === 'pending') {
- project.content.emergency.currentStage = stageNumber + 1;
- }
+ if (stageNumber < project.content.emergency.withdrawalStages.length) {
+ project.content.emergency.currentStage = stageNumber;
+ }Note: currentStage semantics should be clarified - is it 0-indexed or 1-indexed? The current code sets it to stageNumber + 1 after withdrawing stage stageNumber, which could exceed array bounds for the last stage.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Move to next stage | |
| const nextStage = project.content.emergency.withdrawalStages[stageNumber]; | |
| if (nextStage && nextStage.status === 'pending') { | |
| project.content.emergency.currentStage = stageNumber + 1; | |
| } | |
| // Move to next stage | |
| if (stageNumber < project.content.emergency.withdrawalStages.length) { | |
| project.content.emergency.currentStage = stageNumber; | |
| } |
🤖 Prompt for AI Agents
In src/routes/ProjectDetails.svelte around lines 772–776, the progression logic
assumes stageNumber indexing but risks off-by-one and out-of-bounds when
advancing currentStage; treat stageNumber as 1-based: compute nextIndex =
stageNumber (numeric) and only access withdrawalStages[nextIndex] if nextIndex <
withdrawalStages.length; if nextStage exists and its status === 'pending' set
currentStage = stageNumber + 1 (1-based), otherwise if no next stage set
currentStage to withdrawalStages.length (or null/finished sentinel) to avoid
exceeding array bounds; add a comment clarifying that currentStage is 1-indexed.
| {:else} | ||
| <!-- Normal Actions (only shown if not blocked) --> | ||
| <div class="action-buttons"> | ||
| {#if verificationPending} | ||
| <Alert class="bg-yellow-50 dark:bg-yellow-900/20 border-yellow-500 mb-4"> | ||
| <AlertDescription> | ||
| ⏳ <strong>Verification Pending:</strong> This emergency campaign is currently under community review. Donations will be enabled once verification reaches 60% approval with minimum 20 votes. | ||
| </AlertDescription> | ||
| </Alert> | ||
| {:else if verificationRejected} | ||
| <Alert class="bg-red-50 dark:bg-red-900/20 border-red-500 mb-4"> | ||
| <AlertDescription> | ||
| ❌ <strong>Verification Failed:</strong> This campaign did not pass community verification. Donations are disabled. | ||
| </AlertDescription> | ||
| </Alert> | ||
| {/if} |
There was a problem hiding this comment.
Unreachable code: verificationPending alert inside else block.
The {:else} block at line 1255 only executes when the campaign is NOT in PENDING_VERIFICATION, UNDER_REVIEW, or REJECTED phases. However, lines 1258-1263 check verificationPending which would only be true for those exact phases. This alert will never be displayed:
{:else}
<!-- Normal Actions (only shown if not blocked) -->
<div class="action-buttons">
- {#if verificationPending}
- <Alert class="bg-yellow-50 dark:bg-yellow-900/20 border-yellow-500 mb-4">
- <AlertDescription>
- ⏳ <strong>Verification Pending:</strong> ...
- </AlertDescription>
- </Alert>
- {:else if verificationRejected}
- <Alert class="bg-red-50 dark:bg-red-900/20 border-red-500 mb-4">
- <AlertDescription>
- ❌ <strong>Verification Failed:</strong> ...
- </AlertDescription>
- </Alert>
- {/if}
+ <!-- Alerts moved to verification-block sections above -->The verification alerts are already shown in the verification-block divs at lines 1227-1254.
🤖 Prompt for AI Agents
In src/routes/ProjectDetails.svelte around lines 1255 to 1270, the
verificationPending/verificationRejected Alert block is placed inside an {:else}
that only runs when the campaign is NOT in
PENDING_VERIFICATION/UNDER_REVIEW/REJECTED, so those alerts are unreachable and
duplicate the alerts in the verification-block (lines 1227–1254); remove this
unreachable verification alert block (or move its condition out of the {:else}
into the main flow) so verification messages are only rendered once in the
verification-block and the {:else} only contains actions relevant when the
campaign is not under verification/rejected.
| async function submitProof() { | ||
| if (!$walletConnected || !selectedStage) return; | ||
|
|
||
| if (proofHashes.length === 0) { | ||
| errorMessage = "Please add at least one proof document"; | ||
| return; | ||
| } |
There was a problem hiding this comment.
Missing validation for required proofDescription field.
The UI marks "Proof Description" as required (line 362: *), but the validation only checks proofHashes:
async function submitProof() {
if (!$walletConnected || !selectedStage) return;
if (proofHashes.length === 0) {
errorMessage = "Please add at least one proof document";
return;
}
+ if (!proofDescription.trim()) {
+ errorMessage = "Please provide a proof description";
+ return;
+ }
+
submittingProof = true;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function submitProof() { | |
| if (!$walletConnected || !selectedStage) return; | |
| if (proofHashes.length === 0) { | |
| errorMessage = "Please add at least one proof document"; | |
| return; | |
| } | |
| async function submitProof() { | |
| if (!$walletConnected || !selectedStage) return; | |
| if (proofHashes.length === 0) { | |
| errorMessage = "Please add at least one proof document"; | |
| return; | |
| } | |
| if (!proofDescription.trim()) { | |
| errorMessage = "Please provide a proof description"; | |
| return; | |
| } |
🤖 Prompt for AI Agents
In src/routes/StagedWithdrawal.svelte around lines 85-91, the submitProof
function currently only validates proofHashes; add a check that proofDescription
is present (non-empty after trimming) and, if missing, set errorMessage to
something like "Please provide a proof description" and return. Ensure this
validation runs alongside the existing proofHashes check (order can be either
before or after) so the UI-required field prevents submission when empty.
| <div class="w-full bg-gray-200 rounded-full h-2"> | ||
| <div | ||
| class="bg-green-500 h-2 rounded-full" | ||
| style="width: {(stage.votesFor / (stage.votesFor + stage.votesAgainst)) * 100}%" | ||
| ></div> | ||
| </div> |
There was a problem hiding this comment.
Division by zero when displaying vote progress bar.
If both votesFor and votesAgainst are 0 (which can happen when stage is first requested per line 105-106), this calculation will divide by zero:
<div class="w-full bg-gray-200 rounded-full h-2">
<div
class="bg-green-500 h-2 rounded-full"
- style="width: {(stage.votesFor / (stage.votesFor + stage.votesAgainst)) * 100}%"
+ style="width: {(stage.votesFor + stage.votesAgainst) > 0
+ ? (stage.votesFor / (stage.votesFor + stage.votesAgainst)) * 100
+ : 0}%"
></div>
</div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div class="w-full bg-gray-200 rounded-full h-2"> | |
| <div | |
| class="bg-green-500 h-2 rounded-full" | |
| style="width: {(stage.votesFor / (stage.votesFor + stage.votesAgainst)) * 100}%" | |
| ></div> | |
| </div> | |
| <div class="w-full bg-gray-200 rounded-full h-2"> | |
| <div | |
| class="bg-green-500 h-2 rounded-full" | |
| style="width: {(stage.votesFor + stage.votesAgainst) > 0 | |
| ? (stage.votesFor / (stage.votesFor + stage.votesAgainst)) * 100 | |
| : 0}%" | |
| ></div> | |
| </div> |
🤖 Prompt for AI Agents
In src/routes/StagedWithdrawal.svelte around lines 219-224 the inline style
computes width as (stage.votesFor / (stage.votesFor + stage.votesAgainst)) * 100
which will divide by zero when both counts are 0; change it to compute a safe
percent value first (e.g. let total = stage.votesFor + stage.votesAgainst; let
percent = total > 0 ? (stage.votesFor / total) * 100 : 0) and use that percent
in the style so you never divide by zero and the progress bar shows 0% when
there are no votes.
|
Hey @SUHANI-PAL0103 Could you explain a little more this idea? |
Summary
Implements Phase 2 of the 4-layer fraud prevention system - Community Voting with 60% approval threshold.
Changes
New Files
src/lib/ergo/voting/voting_contract.ts- VotingContract class with localStorage-based votingsrc/routes/CommunityVoting.svelte- Voting UI component with progress barssrc/routes/StagedWithdrawal.svelte- Staged fund release managementsrc/routes/Phase2Demo.svelte- Demo component for Phase 2 featuresVOTING_SYSTEM_IMPLEMENTATION.md- DocumentationModified Files
src/lib/common/project.ts- CRITICAL FIX: Extract emergency data from blockchain contentsrc/lib/ergo/platform.ts- Fix BigInt conversion for wallet balancesrc/routes/NewProject.svelte- Add emergency data to all campaigns by defaultsrc/routes/ProjectDetails.svelte- Integrate voting interface, block donations until approvedsrc/routes/ProjectCard.svelte- Fix BigInt balance displaysrc/routes/ProjectList.svelte- Disable test project filter by defaultFeatures
Testing
Related Issues
Implements Phase 2 of the fraud prevention roadmap.
Summary by CodeRabbit
New Features
Documentation
Improvements
✏️ Tip: You can customize this high-level summary in your review settings.