Skip to content

Instantly share code, notes, and snippets.

@johnfkneafsey
Created March 16, 2017 04:23
Show Gist options
  • Save johnfkneafsey/7e4f3cc1f36846e3993f0e8631dff15d to your computer and use it in GitHub Desktop.
Save johnfkneafsey/7e4f3cc1f36846e3993f0e8631dff15d to your computer and use it in GitHub Desktop.
// 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