Decentralized voting on Ethereum: what we tried, archived honestly.
By Timothy Mo ·
We built a decentralized voting system on Ethereum + .NET C#. It was a working proof-of-concept. It was not a good idea. Here is the honest framing we should have led with.
We once shipped a working proof-of-concept for a decentralized voting system using Ethereum smart contracts (Solidity) and a .NET C# application built with Nethereum. The PoC was technically clean. It worked. The framing we wrote it up with was wrong, and we owe it a correction rather than a takedown.
What we built
A Solidity contract for candidate registration, vote casting, and result tallying. A .NET C# application that talked to the contract via Nethereum. An ASP.NET front end for voters and election admins. Local Ganache for development; mainnet-capable on deployment.
It worked end-to-end. Candidates registered, votes cast, results immutable on-chain.
Why we wrote about it
There was real client interest in “blockchain voting” as a category at the time. Two clients asked us to scope the work. We built the PoC partly as a sales artefact and partly because it was an interesting weekend project. The original write-up was a marketing-adjacent technical write-up.
Why it was a bad idea
The election-security and democracy-research community had been writing about why this category of system is a bad idea since at least 2019. We knew the literature. We wrote the article anyway. With the benefit of hindsight, here’s what the original under-said:
1. “Tamper-proof” is not the binding constraint of voting integrity
We emphasised that “the decentralized nature of the Ethereum blockchain ensures that no single party can manipulate the voting process.” That sentence is technically defensible and operationally misleading. Real-world voting integrity is constrained by:
- Voter identity verification (who is casting the vote).
- End-device security (the laptop or phone the vote is cast from).
- Software-supply-chain trust (did the voter receive the contract you wrote, or one substituted for it).
- Coercion resistance (was the voter free to vote as they chose).
A blockchain solves none of these. It solves “is the tally calculated honestly given the inputs,” which has rarely been the failure mode in real elections.
2. Anonymity on a public chain is hard
Our write-up claimed the smart contract “maintains voter identity privacy.” For a small electorate, public-chain transaction patterns leak identity. Real anonymity on a public chain requires zero-knowledge constructions (Semaphore, MACI, etc.) the original article did not mention.
3. Gas, again
For any non-trivial election size, the gas cost makes the on-chain path uneconomic. We did not calculate this in the original. We should have.
Where this kind of work might be appropriate
For real elections: not at all, in our view. The serious election-tech world uses paper ballots with risk-limiting audits. Software is not the limiting factor.
For small-scale internal corporate or community votes where the use case is “low-stakes board polling, with public-record optics,” there is a real niche. We have built one such system since for an industry consortium. It uses MACI-style zk for ballot privacy and an L2 chain (Optimism) for cost. It is much closer to “novelty governance tool” than to “election infrastructure.”
What we’d say instead
We built a small Ethereum-based voting PoC to scope a couple of client conversations. It works. It is not the right shape for any real election. Here is what we’d build for a low-stakes internal vote, and what we’d not touch with a barge pole.
That is the honest framing. Publishing it is the point of this note.
The Solidity + Nethereum technical pattern is fine for low-stakes internal use cases. The technique is fine; the pitch was not.
— Timothy Mo, wGrow