Created
March 16, 2017 04:23
-
-
Save johnfkneafsey/7e4f3cc1f36846e3993f0e8631dff15d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// define version | |
pragma solidity ^0.4.2; | |
// defines the "owned" contract | |
contract owned { | |
// defines the "variable" as an "address" and makes it public *** Need more info on public vs non public | |
address public owner; | |
// defines the owner of the contract as the sender *** The original sender? Any sender? It's not obvious here but I think I know the answer. | |
function owned() { | |
owner = msg.sender; | |
} | |
// "modifier" that says the function is only executed if the owner calls it. If someone else calls it, an error is thrown | |
modifier onlyOwner { | |
if (msg.sender != owner) throw; | |
_; | |
} | |
// gives the ability to transfer the owner of the contract but only works if called by the owner since it includes the "onlyOwner" modifier. This is aka "forbidden function" | |
function transferOwnership(address newOwner) onlyOwner { | |
owner = newOwner; | |
} | |
} | |
// END "owned" CONTRACT | |
// defines the "tokenRecipient" contract, which allows you to receive tokens in exchange for ether *** Need to confirm that is all this does | |
contract tokenRecipient { | |
// defines the "receivedEther" event. Expects 2 parameters, "sender" (an address) and "amount" (an unsigned integer) | |
event receivedEther(address sender, uint amount); | |
// defines the "receivedTokens" event. Expects 4 parameters, "_from" (an address), "_value" (an unsigned integer), "_token" (an address) and "_extraData" (additional messaging in byte format) | |
event receivedTokens(address _from, uint256 _value, address _token, bytes _extraData); | |
// this function that seems to allow users to send tokens on other users' behalf. Doesnt seem to be called anywhere. Seems to be a planned feature (https://github.com/ethereum/ethereum-org/issues/251) | |
// Not going to delve in to at this point. | |
function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData){ | |
Token t = Token(_token); | |
if (!t.transferFrom(_from, this, _value)) throw; | |
receivedTokens(_from, _value, _token, _extraData); | |
} | |
// a "fallback function". This function will trigger when ether is received. Most contracts include this line so that they can throw an error when they receive ether inadvertently. | |
// However, we are expecting ether in this case. Therefore, we add the "payable" line. Since "receivedEther" is an event, it will be recorded in the transaction's log. We will be able to watch | |
// this event through our JS API to see transactions. | |
function () payable { | |
receivedEther(msg.sender, msg.value); | |
} | |
} | |
// defines Token contract | |
contract Token { | |
// function that allows you to transfer x amount of tokens from one user to another. self explanatory variables. | |
function transferFrom(address _from, address _to, uint256 _value) returns (bool success); | |
} | |
// defines Congress contract. "is owned" allows the "onlyOwner" modifier defined at the beginning to be used in functions inside the Congress contract. *** What is tokenRecipient doing for us here??? | |
contract Congress is owned, tokenRecipient { | |
// Congress contract variables. Also maps a list of congress members. | |
uint public minimumQuorum; | |
uint public debatingPeriodInMinutes; | |
int public majorityMargin; | |
// [] is used to define an array of dynamic size. An array of fixed size (5) would look like this: Proposal[5]. I assume this is a dynamic array because there can be multiple active proposals at once. | |
Proposal[] public proposals; | |
uint public numProposals; | |
// this maps every user/member address to an integer and stores it as their memberID | |
mapping (address => uint) public memberId; | |
// An array (dynamic) of members | |
Member[] public members; | |
// defines the various events that can occur in our congress. variables are assumed to be self explanatory here. We can listen in on these through our JS API. | |
event ProposalAdded(uint proposalID, address recipient, uint amount, string description); | |
event Voted(uint proposalID, bool position, address voter, string justification); | |
event ProposalTallied(uint proposalID, int result, uint quorum, bool active); | |
event MembershipChanged(address member, bool isMember); | |
event ChangeOfRules(uint minimumQuorum, uint debatingPeriodInMinutes, int majorityMargin); | |
// structs are like custom "types". They are used to group variables. Very object-like. "Proposal" defines the variables needed to propose something in the congress. | |
struct Proposal { | |
address recipient; | |
uint amount; | |
string description; | |
uint votingDeadline; | |
bool executed; | |
bool proposalPassed; | |
uint numberOfVotes; | |
int currentResult; | |
bytes32 proposalHash; | |
Vote[] votes; | |
mapping (address => bool) voted; | |
} | |
// struct that defines what contitutes a "member" | |
struct Member { | |
address member; | |
string name; | |
uint memberSince; | |
} | |
// struct that defines what contitutes a "vote". I like the ability to add a string to explain your choice. | |
struct Vote { | |
bool inSupport; | |
address voter; | |
string justification; | |
} | |
// modifier that allows only shareholders to vote and create new proposals. Used later on. | |
modifier onlyMembers { | |
if (memberId[msg.sender] == 0) | |
throw; | |
_; | |
} | |
// first time set up variables. | |
function Congress( | |
uint minimumQuorumForProposals, | |
uint minutesForDebate, | |
int marginOfVotesForMajority, address congressLeader | |
) payable { | |
changeVotingRules(minimumQuorumForProposals, minutesForDebate, marginOfVotesForMajority); | |
if (congressLeader != 0) owner = congressLeader; | |
// It’s necessary to add an empty first member | |
addMember(0, ''); | |
// adds the founder, can also be done later on | |
addMember(owner, 'founder'); | |
} | |
// function that adds members. Can only be called by the owner. Fairly self explanatory. calls on the "Member" struc at the end of the "if" statement to officially create the member | |
function addMember(address targetMember, string memberName) onlyOwner { | |
uint id; | |
if (memberId[targetMember] == 0) { | |
memberId[targetMember] = members.length; | |
id = members.length++; | |
members[id] = Member({member: targetMember, memberSince: now, name: memberName}); | |
} else { | |
id = memberId[targetMember]; | |
Member m = members[id]; | |
} | |
MembershipChanged(targetMember, true); | |
} | |
// function that removes members. Self explanatory. | |
function removeMember(address targetMember) onlyOwner { | |
if (memberId[targetMember] == 0) throw; | |
for (uint i = memberId[targetMember]; i<members.length-1; i++){ | |
members[i] = members[i+1]; | |
} | |
delete members[members.length-1]; | |
members.length--; | |
} | |
// function that can only be called by the member that allows them to change the voting rules defined at the inception of the congress. | |
function changeVotingRules( | |
uint minimumQuorumForProposals, | |
uint minutesForDebate, | |
int marginOfVotesForMajority | |
) onlyOwner { | |
minimumQuorum = minimumQuorumForProposals; | |
debatingPeriodInMinutes = minutesForDebate; | |
majorityMargin = marginOfVotesForMajority; | |
ChangeOfRules(minimumQuorum, debatingPeriodInMinutes, majorityMargin); | |
} | |
// function that adds a new proposal to the proposal list and then gathers the necessary information needed to make a proposal and adds it to the proposal. Again, very similar to objects. ex: p.amount = amount | |
function newProposal( | |
address beneficiary, | |
uint etherAmount, | |
string JobDescription, | |
bytes transactionBytecode | |
) | |
onlyMembers | |
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; | |
return proposalID; | |
} | |
// function to check if a proposal code matches *** not really sure what that means. Not sure if this is a core feature or security-related check. | |
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); | |
} | |
// the vote function. key parts described in more detail below. | |
function vote( | |
uint proposalNumber, | |
bool supportsProposal, | |
string justificationText | |
) | |
onlyMembers | |
returns (uint voteID) | |
{ | |
Proposal p = proposals[proposalNumber]; // Get the proposal | |
if (p.voted[msg.sender] == true) throw; // If has already voted, cancel | |
p.voted[msg.sender] = true; // Set this voter as having voted | |
p.numberOfVotes++; // Increase the number of votes | |
if (supportsProposal) { // If they support the proposal | |
p.currentResult++; // Increase score | |
} else { // If they don't | |
p.currentResult--; // Decrease the score | |
} | |
// Create a log of this event so we can access it in JS | |
Voted(proposalNumber, supportsProposal, msg.sender, justificationText); | |
return p.numberOfVotes; | |
} | |
function executeProposal(uint proposalNumber, bytes transactionBytecode) { | |
Proposal p = proposals[proposalNumber]; | |
// Check if the proposal can be executed: | |
// Has the voting deadline arrived? | |
// Has it been already executed or is it being executed? | |
// Does the transaction code match the proposal? | |
// Has a minimum quorum? | |
// check the validity of the proposal to be executed. throw error if anything seems off. | |
if (now < p.votingDeadline | |
|| p.executed | |
|| p.proposalHash != sha3(p.recipient, p.amount, transactionBytecode) | |
|| p.numberOfVotes < minimumQuorum) | |
throw; | |
// execute result | |
// If difference between support and opposition is larger than margin | |
if (p.currentResult > majorityMargin) { | |
// Avoid recursive calling | |
p.executed = true; | |
if (!p.recipient.call.value(p.amount * 1 ether)(transactionBytecode)) { | |
throw; | |
} | |
p.proposalPassed = true; | |
} else { | |
p.proposalPassed = false; | |
} | |
// Fire Events | |
ProposalTallied(proposalNumber, p.currentResult, p.numberOfVotes, p.proposalPassed); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment