# 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.