# Binding ERC20 offchain voting
On this proposal we leverage on zkSnarks. Unfortunatelly secp256k1 and keccack256 are not snark friendly cryptographic primitives. To this end we need to create a mechanism to which every user that wants to participate on the governance will create a new pair of snark friendly private and public keys. The private key might be deterministic and derivated from the main Ethereum key.
We need a public registry (smart contract) that stores the relation:
`ethereum_address => zkSnark_friendly_pubKey`
Anybody can create a new `zkSnark_friendly` privateKey and register itself to that registry.
Once a new voting process is created for a specific ERC20 token (identified by a unique electionId), somebody must construct an offchain a merkleTree (census) with the list of `zkSnark_friendly_pubKey` list. If a token holder is not registered, they won't be included in the census. The merkleTree needs to be also snark friendly and store `PubKeys` as indexes and `balances` as values (leafs).
The process for constructing the census would be:
1. Get token holders for block N
2. For each holder, check if `zkSnark_friendly_pubKey` exist
3. If exists, add it to the merkle tree with its the token balance on block N
Once the census merkle tree is constructed, the merkle root becames the **censusRoot**.
The voting can then be handled offchain, a user needs to provide the franchise proof (merkle proof proving its key is included in the census merkle tree) and a nullifier (signature of the electionId) in order to cast a vote. A vote is represented as an array of natural numbers, such as `[1,1,3]`
A zkRollup will aggregate all casted votes: For a number of votes, for a censusRoot, for an election identifier (electionId) and for a numeric result, the zkRollup proof is valid.
**zkRollup INPUTS**
-> censusRoot (PUB)
-> electionId (PUB)
-> nullifiers[1..N] (PRIV)
-> #nullifiers (PUB)
-> votes[1..N] (PRIV)
-> result (PUB)
The franchise proof might also be a zkSnark (anonymizing the user), but that would require one level of recursivity, so lets forget about this feature for now.
The zkRollup proof can then be sent to a smart contract which needs to provide an optimistic rollup mechanism: wait for some time before considering the results valid. During this period, anybody can challenge the results by providing a proof of fraud.
1. A different zkRollup proof with a bigger number of nullifiers included
2. An EIP1186 storage proof, proving a token holder registered in the zkSnark_friendly registry on block N was not included into the census merkleTree. Or a proof proving someone was included without being a token holder.
If after that period no one challenges the result, it is considered valid and binding actions can be executed on a smart contract.