“‘Talent Is Equally Distributed, But Opportunity Is Not’ — this is true. As a founder, most investors are in Silicon Valley. Some write checks to students. Shouldn’t we give every deserving person a chance? Wouldn’t you buy a stake in young Elon Musk? Whether Income Sharing Agreement (ISA) or ‘freelancer time’ to offer work, you can sell some of your future self.
Personal tokens let people fulfill their potential by getting financial and professional support. I believe it reflects Ethereum’s inclusiveness, as any person bringing value can rise up in our community. I felt supported myself, and creating my token $ALEX was a logical step. It was technically easy to leverage my supporters: I raised $20,000 in 5 days with zero friction.
However, one side of personal tokens is lacking infrastructure: community governance, or ‘guidance’ when advising an individual. Overall I’m fascinated by the future of this space, even beyond entrepreneurs: music artists, athletes, influencers…”
🤔 How It Works
So you have a personal token (ERC20) deployed and you want your holders to be able to vote on a proposal. In this demo we will assume that it is okay to do most things off-chain and we will lean back on handy web2 technologies.
However, we will use the power of signed messages and let any user “vote” by signing their preference and sending it to us. Then, at a certain, predetermined time, we can cryptographically validate votes with the signature and weight the vote by the amount of tokens the user holds.
🙇♀️ Getting Started
Let’s start with the
emoji-vote-dev branch of 🏗 scaffold-eth :
git clone https://github.com/austintgriffith/scaffold-eth.gitcd scaffold-ethgit checkout emoji-vote-devyarn installyarn start
☢️ Warning, you may see node-gyp errors, ignore those and try
This will bring up a sample emoji voting app that you can fork and change:
First, a user will log in with some sort of web3 wallet or use a burner:
Then, they can click an option to sign their vote with their web3 account:
You can edit what the frontend, in particular the voting options, in the folder
packages/react-app/src in the file
You can also edit the page header/title in
These votes are being sent to a Zapier web hook that you can edit here too.
This is handy because then we don’t need to run a backend server.
Sign in to https://zapier.com/ and use the big [ + ] button in the top left to “Create a Zap”.
(Here is my prebuilt Zap if that will save you some button clicks.)
Select “Webhooks by Zapier” then set the Zap to “Catch Hook”:
Continue and a Webhook will be created for you, copy it:
packages/react-app/src and change the
get to your hook:
Hit save and your app should reload.
Make a vote so we can send some test data to your new hook:
After you have voted, continue on in Zapier to “Test Trigger”:
If we’ve done everything right, you will see data from your test vote:
Nice! Let’s take a second to celebrate with some emojis: 🍾 🥂 🎉 🥳
Now let’s continue to the “Do this …” part and tell Zapier to add a row to a Google Sheet when new data hits our hook.
You will need to create a Google Sheet so it shows up in the next step.
If the Zap finds your sheet you will get a message that we need to add some headers:
“Freeze” one row using the
view menu and then enter these headers:
address vote timestamp signature
You will want to refresh the fields and then wire them together like:
💡Neat, it uses the fields from our test request to let us fill in where the data will go in the spreadsheet.
⚡️ Turn your Zap on:
Now, whenever we make a vote in our app, we should see it show up in the google sheet along with a signature:
We can cryptographically prove that a specific key pair has signed a specific vote string. For instance, the string “emojivoteDOG1590077153770” can be signed and this information is tamperproof. If anything changes, the derived address will be different.
💡 This vote string, along with a signature can be used by anyone to derive the voter’s address. 📚 Learn more about how key pairs can sign and encrypt.
💬 Like my homie, Dan Finlay says, “Make sure your signature challenge is very unique to this poll to avoid replay attacks.”
That’s right! See that “emojivote” up there? That needs to be unique for every poll or you will get votes from one that can work in another one. Maybe put the question of the poll in the vote string?
🛠 Try this eth.build to manually verify signatures at first. We can copy and paste values from our spreadsheet into the build to see if the address field is right and also how many of our tokens the voter has:
😎 Rad, now we have signed votes showing up in a familiar interface like google sheets. We could probably verify and count the votes from here, but let’s try to 🤖 automate a little more…
⚙️ Validate Script
There is a simple
index.js script in
packages/emojivote-validator that will attempt to read, validate, and count totals for votes. You can edit this script and add your sheet’s ID:
Then we need to allow access to our script.
Follow this link and click the “Enable the Google Sheets API” button:
Tell it you want a “Desktop App” and click “Create”.
Now “Download Client Configuration”:
Put this file here:
Now we can run our validation script. In the root project directory, let’s run this command which should fire up our script:
yarn run validate
(If you are in
packages/emojivote-validator you can run
node index too.)
When it runs, it will find that we don’t have Google auth yet:
Copy and paste that link into a browser to get an auth token. If you are logged in with the right Google account, authorizing the app will take you to a modal where you can copy and paste a code. Paste this code back in the terminal.
Once your script has access, you can run
yarn run validate at any time:
💡This script is checking that voters hold a balance of DAI, but you can change this to your personal token by changing
ERC20_TOKEN_ADDRESS in the code.
This will create a file:
packages/react-app/src/validVotes.json that is injected into our frontend and has all the vote totals only for valid signatures that hold the
ERC20_TOKEN_ADDRESS token specified in the code.
🧮 Counting and Displaying Votes
Let’s move back to our frontend and look at the
App.js file in the folder
packages/react-app/src . Toward the bottom you will find two components
VoteReport that are hidden:
display:none and hit save for your app to 🔥 hot reload:
You can use the
TimeReport section to display to your holders when you will trigger the vote count.
You can use the
VoteReport section to display the current valid votes and see who is winning.
Try making a new vote with an account that also owns DAI (or the ERC20) to see an end-to-end voting demo:
You can vote in the frontend with a web3 account, it shows up in your spreadsheet, you validate it with a script, and then the valid vote count is displayed in the frontend.
🚢 Ship it!
Now we want to publish this to our holders. First, build your app:
yarn run build
💡This creates a ‘static site’ in the
build directory of
You can deploy this code to any kind of site host, but we’ll use Surge.sh :
npm install --global surgeyarn run surge
You might have to log in first with an email and password. Then it will have your
project directory already set to your build directory. You just need to specify a
*******.surge.sh domain to publish to:
Then if we visit our .sh domain we should see the app:
Now, your holders should be able to vote on a live site using MetaMask, and you should see the votes show up in your Google Sheet. Then, you can run your validate script to count all the votes. Finally, you can build and deploy the results:
✏️ You can show and hide the different components before you deploy to Surge to control what your holders see.
🗳 This is a rough/quick way to get a vote from your personal token holders without having to do a bunch of on-chain transactions.
🔏 Thanks to public key cryptography we can verify the votes off-chain!