An early DAO made to demonstrate token swaps, mutations, and quadratic voting.
Key Facts
Description
The Unicorn Meat Grinder Association DAO was an early decentralized autonomous organization designed and deployed in 2016 by Alex Van de Sande, then a leading figure at the Ethereum Foundation. The DAO governed a set of smart contracts that allowed holders of the original Unicorn token, an experimental token distributed by the Ethereum Foundation, to swap (“grind”) those tokens for a new asset known as Unicorn Meat. The contract was announced by Ethereum as an April Fool's joke in 2016.
The Unicorn Meat Grinder Association DAO was a decentralized autonomous organization deployed on the Ethereum blockchain in March 2016. It governed a set of smart contracts that allowed holders of the original Unicorn token — a small gratitude token created by the Ethereum Foundation — to exchange (“grind”) those unicorns for a new token called Unicorn Meat. Governance decisions within the DAO were made on-chain using a voting mechanism where token holders could submit and vote on proposals.
Source Verified
DAO controlling the Unicorn Meat grinder rules. Source and compiler settings published by Alex Van de Sande (avsa). Features quadratic voting where each negative vote is worth 4 support votes.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Homestead Era
The first planned hard fork. Removed the canary contract, adjusted gas costs.
Bytecode Overview
Verified Source Available
Source code published by the original contract author.
View Verification ProofShow source code (Solidity)
/*
Solc: '0.2.1-91a6b35f/.-Emscripten/clang/int linked to libethereum-' with optim
Address: 0xc7e9dDd5358e08417b1C88ed6f1a73149BEeaa32
Interface:
[ { "constant": true, "inputs": [ { "name": "", "type": "uint256", "index": 0, "typeShort": "uint", "bits": "256", "displayName": "", "template": "elements_input_uint" } ], "name": "proposals", "outputs": [ { "name": "recipient", "type": "address" }, { "name": "amount", "type": "uint256" }, { "name": "description", "type": "string" }, { "name": "votingDeadline", "type": "uint256" }, { "name": "executed", "type": "bool" }, { "name": "proposalPassed", "type": "bool" }, { "name": "numberOfVotes", "type": "uint256" }, { "name": "proposalHash", "type": "bytes32" } ], "type": "function", "displayName": "proposals" }, { "constant": true, "inputs": [], "name": "rejectionMultiplier", "outputs": [ { "name": "", "type": "uint256", "value": "4", "displayName": "" } ], "type": "function", "displayName": "rejection Multiplier" }, { "constant": false, "inputs": [ { "name": "proposalNumber", "type": "uint256", "index": 0, "typeShort": "uint", "bits": "256", "displayName": "proposal Number", "template": "elements_input_uint" }, { "name": "transactionBytecode", "type": "bytes", "index": 1, "typeShort": "bytes", "bits": "", "displayName": "transaction Bytecode", "template": "elements_input_bytes" } ], "name": "executeProposal", "outputs": [ { "name": "result", "type": "int256" } ], "type": "function", "displayName": "execute Proposal" }, { "constant": true, "inputs": [], "name": "numProposals", "outputs": [ { "name": "", "type": "uint256", "value": "0", "displayName": "" } ], "type": "function", "displayName": "num Proposals" }, { "constant": false, "inputs": [ { "name": "_from", "type": "address", "index": 0, "typeShort": "address", "bits": "", "displayName": " <span class=\"punctuation\">_</span> from", "template": "elements_input_address" }, { "name": "_value", "type": "uint256", "index": 1, "typeShort": "uint", "bits": "256", "displayName": " <span class=\"punctuation\">_</span> value", "template": "elements_input_uint" }, { "name": "_token", "type": "address", "index": 2, "typeShort": "address", "bits": "", "displayName": " <span class=\"punctuation\">_</span> token", "template": "elements_input_address" } ], "name": "receiveApproval", "outputs": [], "type": "function", "displayName": "receive Approval" }, { "constant": false, "inputs": [ { "name": "amountOfUnicornsToGrind", "type": "uint256", "index": 0, "typeShort": "uint", "bits": "256", "displayName": "amount Of Unicorns To Grind", "template": "elements_input_uint" } ], "name": "grindUnicorns", "outputs": [], "type": "function", "displayName": "grind Unicorns" }, { "constant": false, "inputs": [ { "name": "unicornAddress", "type": "address", "index": 0, "typeShort": "address", "bits": "", "displayName": "unicorn Address", "template": "elements_input_address" }, { "name": "meatAddress", "type": "address", "index": 1, "typeShort": "address", "bits": "", "displayName": "meat Address", "template": "elements_input_address" }, { "name": "minimumSharesToPassAVote", "type": "uint256", "index": 2, "typeShort": "uint", "bits": "256", "displayName": "minimum Shares To Pass A Vote", "template": "elements_input_uint" }, { "name": "minutesForDebate", "type": "uint256", "index": 3, "typeShort": "uint", "bits": "256", "displayName": "minutes For Debate", "template": "elements_input_uint" }, { "name": "multiplierForVotesAgainst", "type": "uint256", "index": 4, "typeShort": "uint", "bits": "256", "displayName": "multiplier For Votes Against", "template": "elements_input_uint" } ], "name": "changeVotingRules", "outputs": [], "type": "function", "displayName": "change Voting Rules" }, { "constant": true, "inputs": [], "name": "unicornTokenAddress", "outputs": [ { "name": "", "type": "address", "value": "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7", "displayName": "" } ], "type": "function", "displayName": "unicorn Token Address" }, { "constant": true, "inputs": [ { "name": "x", "type": "uint256", "index": 0, "typeShort": "uint", "bits": "256", "displayName": "x", "template": "elements_input_uint" } ], "name": "sqrt", "outputs": [ { "name": "y", "type": "uint256", "value": "0", "displayName": "y" } ], "type": "function", "displayName": "sqrt" }, { "constant": true, "inputs": [], "name": "debatingPeriodInMinutes", "outputs": [ { "name": "", "type": "uint256", "value": "0", "displayName": "" } ], "type": "function", "displayName": "debating Period In Minutes" }, { "constant": true, "inputs": [], "name": "minimumQuorum", "outputs": [ { "name": "", "type": "uint256", "value": "1", "displayName": "" } ], "type": "function", "displayName": "minimum Quorum" }, { "constant": true, "inputs": [ { "name": "", "type": "address", "index": 0, "typeShort": "address", "bits": "", "displayName": "", "template": "elements_input_address" } ], "name": "unicornsKilled", "outputs": [ { "name": "", "type": "uint256", "value": "0", "displayName": "" } ], "type": "function", "displayName": "unicorns Killed" }, { "constant": true, "inputs": [], "name": "owner", "outputs": [ { "name": "", "type": "address", "value": "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb", "displayName": "" } ], "type": "function", "displayName": "owner" }, { "constant": false, "inputs": [ { "name": "beneficiary", "type": "address", "index": 0, "typeShort": "address", "bits": "", "displayName": "beneficiary", "template": "elements_input_address" }, { "name": "etherAmount", "type": "uint256", "index": 1, "typeShort": "uint", "bits": "256", "displayName": "ether Amount", "template": "elements_input_uint" }, { "name": "JobDescription", "type": "string", "index": 2, "typeShort": "string", "bits": "", "displayName": " Job Description", "template": "elements_input_string" }, { "name": "transactionBytecode", "type": "bytes", "index": 3, "typeShort": "bytes", "bits": "", "displayName": "transaction Bytecode", "template": "elements_input_bytes" } ], "name": "newProposal", "outputs": [ { "name": "proposalID", "type": "uint256" } ], "type": "function", "displayName": "new Proposal" }, { "constant": false, "inputs": [ { "name": "proposalNumber", "type": "uint256", "index": 0, "typeShort": "uint", "bits": "256", "displayName": "proposal Number", "template": "elements_input_uint" }, { "name": "supportsProposal", "type": "bool", "index": 1, "typeShort": "bool", "bits": "", "displayName": "supports Proposal", "template": "elements_input_bool" } ], "name": "vote", "outputs": [ { "name": "voteID", "type": "uint256" } ], "type": "function", "displayName": "vote" }, { "constant": true, "inputs": [], "name": "totalUnicornsKilled", "outputs": [ { "name": "", "type": "uint256", "value": "0", "displayName": "" } ], "type": "function", "displayName": "total Unicorns Killed" }, { "constant": true, "inputs": [], "name": "meatTokenAddress", "outputs": [ { "name": "", "type": "address", "value": "0xed6ac8de7c7ca7e3a22952e09c2a2a1232ddef9a", "displayName": "" } ], "type": "function", "displayName": "meat Token Address" }, { "constant": false, "inputs": [ { "name": "newMeatProvider", "type": "address", "index": 0, "typeShort": "address", "bits": "", "displayName": "new Meat Provider", "template": "elements_input_address" } ], "name": "changeMeatProvider", "outputs": [], "type": "function", "displayName": "change Meat Provider" }, { "constant": true, "inputs": [ { "name": "proposalNumber", "type": "uint256", "index": 0, "typeShort": "uint", "bits": "256", "displayName": "proposal Number", "template": "elements_input_uint" }, { "name": "beneficiary", "type": "address", "index": 1, "typeShort": "address", "bits": "", "displayName": "beneficiary", "template": "elements_input_address" }, { "name": "etherAmount", "type": "uint256", "index": 2, "typeShort": "uint", "bits": "256", "displayName": "ether Amount", "template": "elements_input_uint" }, { "name": "transactionBytecode", "type": "bytes", "index": 3, "typeShort": "bytes", "bits": "", "displayName": "transaction Bytecode", "template": "elements_input_bytes" } ], "name": "checkProposalCode", "outputs": [ { "name": "codeChecksOut", "type": "bool", "value": false, "displayName": "code Checks Out" } ], "type": "function", "displayName": "check Proposal Code" }, { "constant": true, "inputs": [], "name": "meatProvider", "outputs": [ { "name": "", "type": "address", "value": "0x4ab274fc3a81b300a0016b3805d9b94c81fa54d2", "displayName": "" } ], "type": "function", "displayName": "meat Provider" }, { "constant": false, "inputs": [ { "name": "newOwner", "type": "address", "index": 0, "typeShort": "address", "bits": "", "displayName": "new Owner", "template": "elements_input_address" } ], "name": "transferOwnership", "outputs": [], "type": "function", "displayName": "transfer Ownership" }, { "inputs": [ { "name": "unicornAddress", "type": "address", "index": 0, "typeShort": "address", "bits": "", "displayName": "unicorn Address", "template": "elements_input_address", "value": "0x89205a3a3b2a69de6dbf7f01ed13b2108b2c43e7" }, { "name": "meatAddress", "type": "address", "index": 1, "typeShort": "address", "bits": "", "displayName": "meat Address", "template": "elements_input_address", "value": "0xED6aC8de7c7CA7e3A22952e09C2a2A1232DDef9A" }, { "name": "minimumUnicornsToPassAVote", "type": "uint256", "index": 2, "typeShort": "uint", "bits": "256", "displayName": "minimum Unicorns To Pass A Vote", "template": "elements_input_uint", "value": "1" }, { "name": "minutesForDebate", "type": "uint256", "index": 3, "typeShort": "uint", "bits": "256", "displayName": "minutes For Debate", "template": "elements_input_uint", "value": "" }, { "name": "multiplierForVotesAgainst", "type": "uint256", "index": 4, "typeShort": "uint", "bits": "256", "displayName": "multiplier For Votes Against", "template": "elements_input_uint", "value": "4" }, { "name": "meatCalculator", "type": "address", "index": 5, "typeShort": "address", "bits": "", "displayName": "meat Calculator", "template": "elements_input_address", "value": "0x4AB274FC3A81B300A0016b3805d9b94C81FA54d2" } ], "type": "constructor" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "proposalID", "type": "uint256" }, { "indexed": false, "name": "recipient", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "description", "type": "string" } ], "name": "ProposalAdded", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "proposalID", "type": "uint256" }, { "indexed": false, "name": "position", "type": "bool" }, { "indexed": false, "name": "voter", "type": "address" } ], "name": "Voted", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "proposalID", "type": "uint256" }, { "indexed": false, "name": "result", "type": "int256" }, { "indexed": false, "name": "quorum", "type": "uint256" }, { "indexed": false, "name": "active", "type": "bool" } ], "name": "ProposalTallied", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "minimumQuorum", "type": "uint256" }, { "indexed": false, "name": "debatingPeriodInMinutes", "type": "uint256" }, { "indexed": false, "name": "sharesTokenAddress", "type": "address" } ], "name": "ChangeOfRules", "type": "event" } ]
testnet unicorn: 0x21E6fc92f93C8A1Bb41e2Be64b4E1f88a54d3576
Meat Grinders Association Address: 0x7cb292Ab6d4170D263609572ee087Bc78b85A92b
Alice: 0xDC9974d8D61EBb673b1D132E0b767f4e38FBA057
Bob: 0x5F8f68a0D1CbC75f6eF764a44619277092C32DF0
Eve: 0xafA55A04adE6645f676F50e45CdE7C90F75Fab99*/
/* The token is used as a voting shares */
contract token {
mapping (address => uint256) public balanceOf;
function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
function mintToken(address target, uint256 mintedAmount);
}
/* define 'owned' */
contract owned {
address public owner;
function owned() {
owner = msg.sender;
}
modifier onlyOwner {
if (msg.sender != owner) throw;
_
}
function transferOwnership(address newOwner) onlyOwner {
owner = newOwner;
}
}
contract MeatCalculator {
function calculateMeat(uint amountOfUnicorns) constant returns (uint amountOfMeat);
}
/* The democracy contract itself */
contract MeatGrindersAssociation is owned {
/* Contract Variables and events */
uint public minimumQuorum;
uint public debatingPeriodInMinutes;
uint public rejectionMultiplier;
Proposal[] public proposals;
uint public numProposals;
mapping (address => uint256) public unicornsKilled;
uint public totalUnicornsKilled;
token public unicornTokenAddress;
token public meatTokenAddress;
MeatCalculator public meatProvider;
event ProposalAdded(uint proposalID, address recipient, uint amount, string description);
event Voted(uint proposalID, bool position, address voter);
event ProposalTallied(uint proposalID, int result, uint quorum, bool active);
event ChangeOfRules(uint minimumQuorum, uint debatingPeriodInMinutes, address sharesTokenAddress);
struct Proposal {
address recipient;
uint amount;
string description;
uint votingDeadline;
bool executed;
bool proposalPassed;
uint numberOfVotes;
bytes32 proposalHash;
Vote[] votes;
mapping (address => bool) voted;
}
struct Vote {
bool inSupport;
uint bribe;
address voter;
}
/* modifier that allows only shareholders to vote and create new proposals */
modifier onlyShareholders {
if (unicornTokenAddress.balanceOf(msg.sender) == 0) throw;
_
}
/* First time setup */
function MeatGrindersAssociation(
address unicornAddress,
address meatAddress,
uint minimumUnicornsToPassAVote,
uint minutesForDebate,
uint multiplierForVotesAgainst,
address meatCalculator
) {
if (minimumUnicornsToPassAVote == 0 ) minimumUnicornsToPassAVote = 1;
changeVotingRules( unicornAddress, meatAddress, minimumUnicornsToPassAVote, minutesForDebate, multiplierForVotesAgainst);
changeMeatProvider(meatCalculator);
}
/*change rules*/
function changeVotingRules(address unicornAddress, address meatAddress, uint minimumSharesToPassAVote, uint minutesForDebate, uint multiplierForVotesAgainst) onlyOwner {
unicornTokenAddress = token(unicornAddress);
meatTokenAddress = token(meatAddress);
minimumQuorum = minimumSharesToPassAVote;
debatingPeriodInMinutes = minutesForDebate;
rejectionMultiplier = multiplierForVotesAgainst;
ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, unicornTokenAddress);
}
function changeMeatProvider(address newMeatProvider) {
meatProvider = MeatCalculator(newMeatProvider);
}
/* Function to create a new proposal */
function newProposal(address beneficiary, uint etherAmount, string JobDescription, bytes transactionBytecode) onlyShareholders returns (uint proposalID) {
proposalID = proposals.length++;
Proposal p = proposals[proposalID];
p.recipient = beneficiary;
p.amount = etherAmount;
p.description = JobDescription;
p.proposalHash = sha3(beneficiary, etherAmount, transactionBytecode);
p.votingDeadline = now + debatingPeriodInMinutes * 1 minutes;
p.executed = false;
p.proposalPassed = false;
p.numberOfVotes = 0;
ProposalAdded(proposalID, beneficiary, etherAmount, JobDescription);
numProposals = proposalID+1;
}
/* function to check if a proposal code matches */
function checkProposalCode(uint proposalNumber, address beneficiary, uint etherAmount, bytes transactionBytecode) constant returns (bool codeChecksOut) {
Proposal p = proposals[proposalNumber];
return p.proposalHash == sha3(beneficiary, etherAmount, transactionBytecode);
}
/* */
function vote(uint proposalNumber, bool supportsProposal) onlyShareholders returns (uint voteID){
Proposal p = proposals[proposalNumber];
if (p.voted[msg.sender] == true) throw;
voteID = p.votes.length++;
p.votes[voteID] = Vote({inSupport: supportsProposal, voter: msg.sender, bribe: sqrt(msg.value + msg.gas*tx.gasprice)});
p.voted[msg.sender] = true;
p.numberOfVotes = voteID +1;
Voted(proposalNumber, supportsProposal, msg.sender);
}
function executeProposal(uint proposalNumber, bytes transactionBytecode) returns (int result) {
Proposal p = proposals[proposalNumber];
/* Check if the proposal can be executed */
if (now < p.votingDeadline /* has the voting deadline arrived? */
|| p.executed /* has it been already executed? */
|| p.proposalHash != sha3(p.recipient, p.amount, transactionBytecode)) /* Does the transaction code match the proposal? */
throw;
/* tally the votes */
uint quorum = 0;
uint yea = 0;
uint nay = 0;
for (uint i = 0; i < p.votes.length; ++i) {
Vote v = p.votes[i];
uint voteWeight = unicornTokenAddress.balanceOf(v.voter);
quorum += voteWeight * v.bribe;
if (v.inSupport) {
yea += voteWeight * v.bribe;
} else {
nay += voteWeight * v.bribe;
}
}
/* execute result */
if (quorum <= minimumQuorum) {
/* Not enough significant voters */
throw;
} else if (yea > nay ) {
/* has quorum and was approved */
p.recipient.call.value(p.amount * 1 ether)(transactionBytecode);
p.executed = true;
p.proposalPassed = true;
} else {
p.executed = true;
p.proposalPassed = false;
}
// Fire Events
ProposalTallied(proposalNumber, result, quorum, p.proposalPassed);
}
function sqrt(uint x) constant returns (uint y) {
if (x == 0) return 0;
else if (x <= 3) return 1;
uint z = (x + 1) / 2;
y = x;
while (z < y)
/// @why3 invariant { to_int !_z = div ((div (to_int arg_x) (to_int !_y)) + (to_int !_y)) 2 }
/// @why3 invariant { to_int arg_x < (to_int !_y + 1) * (to_int !_y + 1) }
/// @why3 invariant { to_int arg_x < (to_int !_z + 1) * (to_int !_z + 1) }
/// @why3 variant { to_int !_y }
{
y = z;
z = (x / z + z) / 2;
}
}
function receiveApproval(address _from, uint256 _value, address _token) {
if(token(_token) != unicornTokenAddress) throw;
if (!unicornTokenAddress.transferFrom(_from, address(this), _value)) throw;
meatTokenAddress.mintToken(_from, meatProvider.calculateMeat(_value));
unicornsKilled[_from] += _value;
totalUnicornsKilled += _value;
}
function grindUnicorns(uint256 amountOfUnicornsToGrind) {
unicornTokenAddress.transferFrom(msg.sender, address(this), amountOfUnicornsToGrind);
meatTokenAddress.mintToken(msg.sender, meatProvider.calculateMeat(amountOfUnicornsToGrind));
unicornsKilled[msg.sender] += amountOfUnicornsToGrind;
totalUnicornsKilled += amountOfUnicornsToGrind;
}
}