Skip to content

Instantly share code, notes, and snippets.

@MidnightLightning
Last active May 11, 2021 16:51
Show Gist options
  • Select an option

  • Save MidnightLightning/d3791c03c9fcdf3c2d935fcc8ea720ab to your computer and use it in GitHub Desktop.

Select an option

Save MidnightLightning/d3791c03c9fcdf3c2d935fcc8ea720ab to your computer and use it in GitHub Desktop.
MoonCat DAO proposal

Mooncats have a unique situation where each and every one of them are uniquely identified and distinct from all the other Mooncats. In most smart-contract-based voting systems/DAOs, Ethereum addresses hold some number of tokens/shares and get some proportional voting weight. But then care has to be taken that in a given voting period for any proposal, that someone cannot vote with their shares, transfer the shares to another address, and then vote again. By having the cats be the voting unit rather than the addresses, MoonCats can solve this issue very neatly.

MoonCat Clubs

Mooncats have Red, Green, and Blue values that range from 0-255. Based on those parameters, they can be separated into different groups and vote as a unit.

Three MoonCat Clubs, one for each of Red, Green, and Blue. They act as a DAO, where the voting rights on proposals are determined by the Red/Green/Blue values of the Cats.

The Club contracts operate like a DAO in that any member can put forth a proposal (in the form of a transaction to be executed on the blockchain), and all members can vote on it for a time, and after a specified voting time has passed, if successful, the Club contract sends that transaction. The upshot is that then the Club contract can be made the "owner" of other contracts (who grant their owner special rights), which effectively makes the child contract "owned" by the Club.

Membership

In order to belong to the Club at all, the cat's color value for that color must be greater than 127. If their color value is greater than this, they can't create proposals or vote at all in the Club.

Genesis cats are granted membership automatically.

Voting weight

A cat's vote counts for (x-127)/8+1. Thus color values are grouped in chunks of 8, but a cat with a value of 255 gets one extra vote (17, compared to 254 which gets a vote weight of 16).

A cat gets an additional 5 points added to their vote weight if they are a named cat, and their name honors the mythical founder of the club (a specific string is found anywhere in their name).

Genesis cats have a vote weight of 20.

Clubs

Raskal's Regal Reds

  • Founder: Raskal Ratter-Ra
  • Key string: "rat" or "Rat"

Giggums' Glorious Greens

  • Founder: Sir Giggums Gobo III
  • Key string "gig" or "Gig"

Bessie's Beautiful Blues

  • Founder: Bessie Belle
  • Key string: "bel" or "Bel"

United Nations of MoonCats

One global DAO where each Mooncat has one vote on each proposal the DAO considers. Genesis cats are not treated differently in this DAO; they have one vote just like all the rest.

This organization serves as the check-and-balance to the other clubs and can serve as a general consensus for the whole community.

pragma solidity ^0.4.16;
contract MoonCatToken {
mapping (bytes5 => address) public catOwners;
}
contract LiquidCatDemocracy {
MoonCatToken public moonCats;
bool underExecution;
bytes5 public highCat;
mapping (bytes5 => uint) public voterId;
mapping (bytes5 => uint256) public voteWeight;
uint public delegatedPercent;
uint public lastWeightCalculation;
uint public numberOfDelegationRounds;
uint public numberOfVotes;
DelegatedVote[] public delegatedVotes;
string public forbiddenFunction;
event NewHighCat(bytes5 newHighCat, bool changed);
struct DelegatedVote {
bytes5 nominee;
bytes5 voter;
}
function LiquidCatDemocracy(
address _token,
string _forbiddenFunction,
uint _percentLossInEachRound
) {
moonCats = MoonCatToken(_token);
delegatedVotes.length++;
delegatedVotes[0] = DelegatedVote({nominee: 0, voter: 0});
forbiddenFunction = _forbiddenFunction;
delegatedPercent = 100 - _percentLossInEachRound;
if (delegatedPercent > 100) delegatedPercent = 100;
}
function nominate(bytes5 _catId, bytes5 _nominee) returns (uint voteIndex) {
require(moonCats.catOwners(_catId) == msg.sender); // Must be the owner of that cat
if (voterId[_catId] == 0) {
voterId[_catId] = delegatedVotes.length;
numberOfVotes++;
voteIndex = delegatedVotes.length++;
numberOfVotes = voteIndex;
} else {
voteIndex = voterId[_catId];
}
delegatedVotes[voteIndex] = DelegatedVote({nominee: _nominee, voter: _catId});
return voteIndex;
}
function executiveOrder(address _target, uint _valueInEther, bytes32 _bytecode) {
require(moonCats.catOwners(highCat) == msg.sender // If caller is owner of the High Cat,
&& !underExecution // and the call is not currently being executed,
&& bytes4(_bytecode) != bytes4(sha3(forbiddenFunction)) // and it's not trying to do the forbidden function
&& numberOfDelegationRounds >= 4); // and delegation has been calculated enough...
underExecution = true;
assert(_target.call.value(_valueInEther * 1 ether)(_bytecode)); // ...then execute the command.
underExecution = false;
}
function changeTokenAddress(address _newTarget) {
require(moonCats.catOwners(highCat) == msg.sender // If caller is owner of the High Cat,
&& numberOfDelegationRounds >= 4); // and delegation has been calculated enough...
moonCats = MoonCatToken(_newTarget); // ...set new token location
}
function calculateVotes() returns (bytes5 winner) {
bytes5 currentWinner = highCat;
uint currentMax = 0;
uint weight = 0;
DelegatedVote storage v = delegatedVotes[0];
if (now > lastWeightCalculation + 90 minutes) {
numberOfDelegationRounds = 0;
lastWeightCalculation = now;
// Distribute the initial weight
for (uint i=1; i< delegatedVotes.length; i++) {
voteWeight[delegatedVotes[i].nominee] = 0;
}
for (i=1; i< delegatedVotes.length; i++) {
voteWeight[delegatedVotes[i].voter] = 1000; // Each cat gets 1000 vote weight, so the delegation percentage per round works
}
}
else {
numberOfDelegationRounds++;
uint lossRatio = 100 * delegatedPercent ** numberOfDelegationRounds / 100 ** numberOfDelegationRounds;
if (lossRatio > 0) {
for (i=1; i< delegatedVotes.length; i++){
v = delegatedVotes[i];
if (v.nominee != v.voter && voteWeight[v.voter] > 0) {
weight = voteWeight[v.voter] * lossRatio / 100;
voteWeight[v.voter] -= weight;
voteWeight[v.nominee] += weight;
}
if (numberOfDelegationRounds>3 && voteWeight[v.nominee] > currentMax) {
currentWinner = v.nominee;
currentMax = voteWeight[v.nominee];
}
}
}
}
if (numberOfDelegationRounds > 3) {
NewHighCat(currentWinner, highCat == currentWinner);
highCat = currentWinner;
}
return currentWinner;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment