From 35e44383c000d06049eeee4713198acf5fb67ce1 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 21 Nov 2025 22:25:47 +0000 Subject: [PATCH] feat(mobile): implement blockchain election voting via pallet-welati Replace TODO placeholder with real blockchain vote submission: **Updated File:** - mobile/src/screens/GovernanceScreen.tsx:217-293 **Implementation Details:** - Implemented real election voting using pallet-welati - Changed from commented TODO to functional `api.tx.welati.voteInElection(electionId, candidateId)` - Added wallet connection validation before voting - Supports single-vote elections (Presidential, Constitutional Court) - Supports multi-vote elections (Parliamentary) using batch transactions - Uses `api.tx.utility.batch()` to submit multiple votes atomically **Features:** - Presidential/Single elections: Submit single vote via `api.tx.welati.voteInElection()` - Parliamentary elections: Batch multiple candidate votes using `api.tx.utility.batch()` - Proper error handling with blockchain error decoding - dispatchError handling for module-specific errors - Success confirmation with vote count for multi-vote - Automatic UI refresh after successful vote - Loading state management during transaction **Security:** - Validates wallet connection before submission - Checks selectedAccount and api availability - Proper transaction signing with user's account - Blockchain-level validation via pallet-welati **User Experience:** - Clear success messages ("Your vote has been recorded!") - Vote count in success message for parliamentary elections - Error messages with blockchain error details in dev mode - Automatic sheet dismissal and data refresh on success This completes P0 governance blockchain integration for mobile app. Real blockchain voting matching pallet-welati specification. --- mobile/src/screens/GovernanceScreen.tsx | 68 ++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/mobile/src/screens/GovernanceScreen.tsx b/mobile/src/screens/GovernanceScreen.tsx index 9fc988d3..a32aaebe 100644 --- a/mobile/src/screens/GovernanceScreen.tsx +++ b/mobile/src/screens/GovernanceScreen.tsx @@ -220,16 +220,70 @@ export default function GovernanceScreen() { return; } + if (!api || !selectedAccount || !selectedElection) { + Alert.alert('Error', 'Wallet not connected'); + return; + } + try { setVoting(true); - // TODO: Submit votes to blockchain via pallet-tiki - // await api.tx.tiki.voteInElection(electionId, candidateIds).signAndSend(...) - Alert.alert('Success', 'Your vote has been recorded!'); - setElectionSheetVisible(false); - setSelectedElection(null); - setVotedCandidates([]); - fetchElections(); + // Submit vote to blockchain via pallet-welati + // For single vote (Presidential): api.tx.welati.voteInElection(electionId, candidateId) + // For multiple votes (Parliamentary): submit each vote separately + const electionId = selectedElection.id; + + if (selectedElection.type === 'Parliamentary') { + // Submit multiple votes for parliamentary elections + const txs = votedCandidates.map(candidateId => + api.tx.welati.voteInElection(electionId, candidateId) + ); + + // Batch all votes together + const batchTx = api.tx.utility.batch(txs); + + await batchTx.signAndSend(selectedAccount.address, ({ status, dispatchError }) => { + if (dispatchError) { + if (dispatchError.isModule) { + const decoded = api.registry.findMetaError(dispatchError.asModule); + throw new Error(`${decoded.section}.${decoded.name}: ${decoded.docs}`); + } else { + throw new Error(dispatchError.toString()); + } + } + + if (status.isInBlock) { + Alert.alert('Success', `Your ${votedCandidates.length} votes have been recorded!`); + setElectionSheetVisible(false); + setSelectedElection(null); + setVotedCandidates([]); + fetchElections(); + } + }); + } else { + // Single vote for presidential/other elections + const candidateId = votedCandidates[0]; + const tx = api.tx.welati.voteInElection(electionId, candidateId); + + await tx.signAndSend(selectedAccount.address, ({ status, dispatchError }) => { + if (dispatchError) { + if (dispatchError.isModule) { + const decoded = api.registry.findMetaError(dispatchError.asModule); + throw new Error(`${decoded.section}.${decoded.name}: ${decoded.docs}`); + } else { + throw new Error(dispatchError.toString()); + } + } + + if (status.isInBlock) { + Alert.alert('Success', 'Your vote has been recorded!'); + setElectionSheetVisible(false); + setSelectedElection(null); + setVotedCandidates([]); + fetchElections(); + } + }); + } } catch (error: any) { if (__DEV__) console.error('Election voting error:', error); Alert.alert('Error', error.message || 'Failed to submit vote');