transfers

transfers

pragma solidity ^0.4.1;

contract creditCommons {

        // @title creditCommons
        // @author Rogelio SEGOVIA; Matthew Slatter
		// @param sysAdmin is the system administrator address, the creator of the contract
        // @param baseUnits is the number of units before the comma
		address public sysAdmin;
		uint totalNrMembers;
		uint nrGroups;
		uint nrProposals;

	// @notice at creating the contract we declare the general variables
	function creditCommons() {
				// @param the initial sysAdmin is the address from which the contract is created
				sysAdmin = msg.sender;
			    totalNrMembers = 0;
			    nrGroups = 0;
			    nrProposals = 0;
        }

    function getTotals () constant returns (uint, uint, uint) {
    	return (totalNrMembers, nrGroups, nrProposals);
    }

	event NewGroup(address indexed _creator, uint indexed _groupIDN, string _groupNameN, uint _NGTimeStamp);
	event ModifyGroup (address indexed _modifier, uint indexed _groupIDM, string _groupNameM, uint _MGTimeStamp);
	event NewMember (address indexed _memberAddressN, string _memberAliasN, string _descriptionN, uint _NMTimeStamp);
	event JoinGroup (address _memberJG, string _aliasJG, uint _groupJG, string _groupNameJG, uint _JGTimeStamp);
	event ResignGroup (address _memberRG, string _aliasRG, uint _groupRG, string _groupNameRG, uint _RGTimeStamp);
	event Transaction (address indexed _sender, uint _senderAmount, address indexed _receiver, int _receiverAmount, uint _tTimeStamp);
    event ProposalAdded(uint proposalNumber, uint group, string description, address creator);
    event Voted(address voter, uint proposalNumber, int8 vote, int result);
    event ProposalResult(uint proposalNumber, int result, uint quorum, bool active);

	// @notice function to name a new sysAdmin
    function transferSysAdmin(address newSysAdmin) {
		if (msg.sender == sysAdmin) {
        sysAdmin = newSysAdmin;
		}
    }

	// @notice create a structure to file all members
	struct members {
		// @parameter key ID parameters
		bool isMember;
		string alias;
		string whisperID;
		string memberDescription;
		uint memberGroup;
		bool isIntertrade;
		bool isCommune;
		// @parameter balance is expressed in the member currency. Can only be modified by system operations
		int balance;
		uint mDebitLimit;
		uint mCreditLimit;
	}

	// @notice map the members structure into an array indexed by the members ethereum address
	mapping(address => members) member;

	// @notice create an index of members for listing purposes
	address[] memberIndex;

	// @notice anybody with an ethereum account can register in the system
	function registerSystem (string _alias, string _whisperID, string _description) {
		// @notice the caller provides a valid alias
		if (bytes(_alias).length != 0) {
		// @notice the caller is not already the system
			if (member[msg.sender].isMember != true) {
			member[msg.sender].isMember = true;
			member[msg.sender].alias = _alias;
			member[msg.sender].alias = _whisperID;
			member[msg.sender].memberDescription = _description;
			member[msg.sender].memberGroup = 0;
			member[msg.sender].isIntertrade = false;
			member[msg.sender].isCommune = false;
			member[msg.sender].balance = 0;
			member[msg.sender].mDebitLimit = 0;
			member[msg.sender].mCreditLimit = 0;
		NewMember (msg.sender, _alias, _description, now);
		memberIndex [memberIndex.length ++] = msg.sender;
		totalNrMembers = totalNrMembers + 1;
				}
			}
		}

	function modifyMember (string _alias, string _description) {
		if (bytes(_alias).length != 0) {member[msg.sender].alias = _alias;}
		if (bytes(_description).length != 0) {member[msg.sender].memberDescription = _description;}
		}

	function modifyMemberLimits (address _groupMember, uint _newDebitLimit, uint _newCreditLimit) {
		// @notice only the group commune can do
		if (msg.sender == group[member[msg.sender].memberGroup].commune) {
					member[_groupMember].mDebitLimit = _newDebitLimit;
					member[_groupMember].mCreditLimit = _newCreditLimit;
		}
	}

	// @notice anybody in the system can join a group
	function joinGroup (uint _groupJ) {
			// @notice the member is in the system
			if (member[msg.sender].isMember = true) {
				// @notice the group exists
				if (bytes(group[_groupJ].groupName).length != 0){
					// @notice if the group is open
					if (group[_groupJ].open = true) {
					// @notice the member is not in a group
					if (member[msg.sender].memberGroup == 0){
						makeMemberOfGroup (msg.sender, _groupJ);
					}
					}
					// @notice if the group is not open
					else {
                        newProposal (_groupJ, "new Member", member[msg.sender].memberDescription, 30, group[_groupJ].quorum);
					}
			}
		}
	}

	function acceptAtGroup ( address _newMember, uint _groupJ) {
		// @notice the commune can accept a new member in the group
		if (group[_groupJ].commune == msg.sender) {
			makeMemberOfGroup (_newMember, _groupJ);
		}
	}

	function makeMemberOfGroup (address _memberOfGroup, uint _group) internal {
		member[_memberOfGroup].memberGroup = _group;
		member[_memberOfGroup].balance = 0;
		member[_memberOfGroup].mDebitLimit = group[_group].gDebitLimit;
		member[_memberOfGroup].mCreditLimit = group[_group].gCreditLimit;
		group[_group].nrMembers = group[_group].nrMembers + 1;
		JoinGroup (_memberOfGroup, member[_memberOfGroup].alias, _group, group[_group].groupName, now);
	}

	function resignGroup () {
				uint _groupR = member[msg.sender].memberGroup;
				string _groupRN = group[_groupR].groupName;
				// @notice the account is not a group account
				if (group[_groupR].intertradeAccount != msg.sender) {
				// @notice the member is not commune of the group
				if (member[msg.sender].isCommune == false) {
					// @notice the member balance is zero
					if (member[msg.sender].balance == 0) {
						member[msg.sender].memberGroup = 0;
						member[msg.sender].mDebitLimit = 0;
						member[msg.sender].mCreditLimit = 0;
						group[_groupR].nrMembers = group[_groupR].nrMembers - 1;
						ResignGroup (msg.sender, member[msg.sender].alias, _groupR, _groupRN, now);
					}
				}
				}
			}

	function kickOffGroup (address _newMember) {

	}

	function getMember (address _memberG) constant returns (bool, string, string, uint, int, uint, uint) {
	    return (member[_memberG].isMember, member[_memberG].alias, member[_memberG].memberDescription, member[_memberG].memberGroup, member[_memberG].balance, member[_memberG].mDebitLimit, member[_memberG].mCreditLimit);
	}

	function getMemberStatus (address _memberG) constant returns (bool, bool) {
	   return (member[_memberG].isIntertrade, member[_memberG].isCommune);
	}

	function getMemberWhisper (address _memberG) constant returns (string) {
	   return (member[_memberG].whisperID);
	}

	function getNumberMembers () constant returns (uint _nrM) {
		_nrM = totalNrMembers;
	}

	function getMPbyIndex (uint _mIndex) constant returns (address _getMemberID) {
		if (_mIndex < memberIndex.length ++) {
		_getMemberID = memberIndex[_mIndex];
			}
	}

    // @notice create a structure to file all groups and their parameters
    struct groups {
    	string groupName;
    	string groupDescription;
    	string currencyName;
    	address intertradeAccount;
    	address commune;
    	// @parameter the exchange rate against the base currency is given in percentage (100 = 1/1)
    	uint rate;
    	uint gDebitLimit;
    	uint gCreditLimit;
    	bool open;
    	uint nrMembers;
    	uint quorum;
    }

    // @notice map the exchanges structure into an array indexed by a string (the string we use is the CES Exchange ID)
    mapping(uint => groups) group;

    // @notice create an index of exchanges for listing purposes
    uint[] groupIndex;

    // @notice A group can be created by any account in the system that is not in a group. A group is also an account and is identified by its account number. A new group therefore contains two accounts: its own, and its creators.
    function createGroup (string _groupName, string _description, string _currencyName, uint _rate, uint _debitLimit, uint _creditLimit, uint _intertradeDebitLimit, uint _intertradeCreditLimit, bool _open) {
    	// @notice the member exists in the system and the member is not in a group and the name is valid
    	if (member[msg.sender].isMember = true) {
    		if (member[msg.sender].memberGroup == 0) {
    				if (bytes(_groupName).length != 0) {
    					uint groupID = now;
    					group[groupID].groupName = _groupName;
    					group[groupID].currencyName = _currencyName;
    					group[groupID].intertradeAccount = msg.sender;
    					group[groupID].commune = msg.sender;
    					group[groupID].rate = _rate;
    					group[groupID].gDebitLimit = _debitLimit;
    					group[groupID].gCreditLimit = _creditLimit;
    					group[groupID].open = _open;
    					group[groupID].nrMembers = 1;
    					group[groupID].quorum = 3;
    						NewGroup(msg.sender, groupID, _groupName, now);
    						// @notice make the creator member of the group and set the group intertrade limits
    						member[msg.sender].memberGroup = groupID;
    						member[msg.sender].isIntertrade = true;
    						member[msg.sender].isCommune = true;
    						member[msg.sender].balance = 0;
    						member[msg.sender].mDebitLimit = _intertradeDebitLimit;
    						member[msg.sender].mCreditLimit = _intertradeCreditLimit;
    					groupIndex [groupIndex.length ++] = groupID;
    					nrGroups = nrGroups +1;
    				}
    			}
    	    }
    	}

    // @notice transfer group intertrade account. Old intertrade or sysAdmin can transfer group intertrade to another member of the group
    // @notice the reason to include sysAdmin is for the case the old commune disappears
    function transferGroupIntertrade (uint _groupID, address _newIntertrade) {
    	if ((msg.sender == group[_groupID].intertradeAccount) || (msg.sender == sysAdmin)) {
    		if (member[_newIntertrade].memberGroup == _groupID) {
        		member[group[_groupID].intertradeAccount].isIntertrade = false;
        		group[_groupID].intertradeAccount = _newIntertrade;
        		member[_newIntertrade].isIntertrade = true;
    			string _groupName = group[_groupID].groupName;
    			ModifyGroup (msg.sender, _groupID, _groupName, now);
    	}
    	}
    }

    // @notice transfer group commune. Old commune or sysAdmin can transfer group commune to another member of the group
    // @notice the reason to include sysAdmin is for the case the old commune disappears
    function transferGroupCommune (uint _groupID, address _newCommune) {
    	if ((msg.sender == group[_groupID].commune) || (msg.sender == sysAdmin)) {
    		if (member[_newCommune].memberGroup == _groupID) {
        		member[group[_groupID].commune].isCommune = false;
        		group[_groupID].commune = _newCommune;
        		member[_newCommune].isCommune = true;
    			string _groupName = group[_groupID].groupName;
    			ModifyGroup (msg.sender, _groupID, _groupName, now);
    	}
    	}
    }

    // @notice the commune can modify one, several or all parameters of a group. If one parameter is left empty, it remains the same. Only the exchange commune can change its parameters
    function modifyGroup (uint _groupID, string _groupName, string _description, string _currencyName, uint _rate, uint _debitLimit, uint _creditLimit, uint _intertradeDebitLimit, uint _intertradeCreditLimit, bool _open, uint _newQuorum) {
    	        if (msg.sender == group[_groupID].commune) {
    			// @notice if a value for a parameter is given, change the parameter, if empty retain old value
    			if (bytes(_groupName).length != 0) {group[_groupID].groupName = _groupName;}
    			if (bytes(_description).length != 0) {group[_groupID].groupDescription = _description;}
    			if (bytes(_currencyName).length != 0) {group[_groupID].currencyName = _currencyName;}
    			if (_rate != 0) {group[_groupID].rate = _rate;}
    			if (_debitLimit != 0) {group[_groupID].gDebitLimit = _debitLimit;}
    			if (_creditLimit != 0) {group[_groupID].gCreditLimit = _creditLimit;}
    			if (_intertradeDebitLimit != 0) {member[group[_groupID].intertradeAccount].mDebitLimit = _intertradeDebitLimit;}
    			if (_intertradeCreditLimit != 0) {member[group[_groupID].intertradeAccount].mCreditLimit = _intertradeCreditLimit;}
    			if (_open == true) {group[_groupID].open = true;}
    			if (_newQuorum != 0) {group[_groupID].quorum = _newQuorum;}
    			ModifyGroup (msg.sender, _groupID, _groupName, now);
    				}

    }

    function getGroupDescription (uint _groupG) constant returns (string, string, string, bool, uint) {
    return (group[_groupG].groupName, group[_groupG].groupDescription, group[_groupG].currencyName, group[_groupG].open, group[_groupG].nrMembers);
    }

    function getGroupRates (uint _groupG) constant returns (uint, uint, uint) {
    return (group[_groupG].rate, group[_groupG].gDebitLimit, group[_groupG].gCreditLimit);
    }

    function getGroupManagement (uint _groupG) constant returns (address, address, uint, uint) {
    return (group[_groupG].intertradeAccount, group[_groupG].commune, member[group[_groupG].intertradeAccount].mDebitLimit, member[group[_groupG].intertradeAccount].mCreditLimit);
    }

    function getNumberGroups () constant returns (uint _nrG) {
        _nrG = nrGroups;
    }

    function getGroupbyIndex (uint _gIndex) constant returns (uint _getGroupID) {
    	if (_gIndex < groupIndex.length ++) {
    	_getGroupID = groupIndex[_gIndex];
    		}
    }

	// @notice function transfer form the member of the same exchange or to the member of another exchange. The amount is expressed in the sender currency
	function transfer (address _to, uint _fromAmount) {
		// @notice the given amount is converted to integer in order to work with only integers
		int _intFromAmount = int (_fromAmount);
		int _intFromDLimit = - int(member[msg.sender].mDebitLimit);
		int _intToCLimit = int(member[msg.sender].mCreditLimit);
		int _toAmount = 0;
		// @notice check if both accounts are in the same group
		if (member[msg.sender].memberGroup == member[_to].memberGroup) {
			_toAmount = _intFromAmount;
		} else {
			// @notice conversions if the transaction is within groups
			address _fromGroupAccount = group[member[msg.sender].memberGroup].intertradeAccount;
			address _toGroupAccount = group[member[_to].memberGroup].intertradeAccount;
			// @the amount is converted to the receiver currency
			uint _rateSenderU = group[member[msg.sender].memberGroup].rate;
			uint _rateReceiverU = group[member[_to].memberGroup].rate;
			int _rateSender = int(_rateSenderU);
			int _rateReceiver = int(_rateReceiverU);
			_toAmount = _intFromAmount * _rateSender/ _rateReceiver;
			// @notice if the group limits are not surpassed, we proceed with the transfer
			if (((member[_fromGroupAccount].balance - _intFromAmount) > - int(member[_fromGroupAccount].mDebitLimit))
				&& ((member[_toGroupAccount].balance + _toAmount) < int(member[_toGroupAccount].mCreditLimit))) {
				}
		}
		// @notice if the member limits are not surpassed, we proceed with the transfer
			if (((member[msg.sender].balance - _intFromAmount) > _intFromDLimit)
				&& ((member[_to].balance + _toAmount) < _intToCLimit)) {
				member[msg.sender].balance -= _intFromAmount;
				member[_to].balance += _toAmount;
				// @notice adjust intertrade accounts
				if (member[msg.sender].memberGroup != member[_to].memberGroup) {
					member[_fromGroupAccount].balance -= _intFromAmount;
					member[_toGroupAccount].balance += _toAmount;
					}
			}
 			Transaction (msg.sender, _fromAmount, _to, _toAmount, now);
		}

    struct Proposals {
		address creator;
    	uint proposalGroup;
    	string title;
        string description;
        uint votingDeadline;
        uint quorumProposal;
        bool closed;
        bool proposalPassed;
        uint numberOfVotes;
        int currentResult;
		mapping (address => Voters) voters;
    }

	struct Voters {
		bool alreadyVoted;

	}

	mapping (uint => Proposals) proposal;

	// @ notice Function to create a new proposal
    function newProposal (uint _proposalGroup, string _title, string _description, uint _days, uint _quorum) {
        nrProposals ++;
        uint proposalNumber = nrProposals;
		proposal[proposalNumber].creator = msg.sender;
        proposal[proposalNumber].proposalGroup = _proposalGroup;
        proposal[proposalNumber].title = _title;
        proposal[proposalNumber].description = _description;
        proposal[proposalNumber].votingDeadline = now + _days * 1 days;
        proposal[proposalNumber].quorumProposal = _quorum;
        proposal[proposalNumber].closed = false;
        proposal[proposalNumber].proposalPassed = false;
        proposal[proposalNumber].numberOfVotes = 0;
        proposal[proposalNumber].currentResult = 0;
		proposal[proposalNumber].voters[msg.sender].alreadyVoted = false;
        ProposalAdded(proposalNumber, _proposalGroup, _description, msg.sender);
    }

    function vote(uint _proposalNumber, int8 _choice) {
		if (now > proposal[_proposalNumber].votingDeadline) {closeProposal(_proposalNumber);}
		if (proposal[_proposalNumber].closed == false) {
		if (member[msg.sender].memberGroup == proposal[_proposalNumber].proposalGroup) {
					if (proposal[_proposalNumber].voters[msg.sender].alreadyVoted == false) {
        				proposal[_proposalNumber].numberOfVotes += 1;
						proposal[_proposalNumber].voters[msg.sender].alreadyVoted = true;
			if (_choice == 1) {proposal[_proposalNumber].currentResult += 1; }
			if (_choice == -1) {proposal[_proposalNumber].currentResult -= 1; }
        // Create a log of this event
        Voted(msg.sender, _proposalNumber, _choice, proposal[_proposalNumber].currentResult);
					}
					}
				}
			}

    function closeProposal(uint _proposalNumber) {
        /* If difference between support and opposition is larger than margin */
		if (now > proposal[_proposalNumber].votingDeadline) {
		if (proposal[_proposalNumber].closed == false) {
        if ((proposal[_proposalNumber].numberOfVotes > proposal[_proposalNumber].quorumProposal)
        		|| (proposal[_proposalNumber].currentResult > 0))
        {
            proposal[_proposalNumber].proposalPassed = true;
        } else {
            proposal[_proposalNumber].proposalPassed = false;
        }
        // Fire Events
        ProposalResult(_proposalNumber, proposal[_proposalNumber].currentResult, proposal[_proposalNumber].numberOfVotes, proposal[_proposalNumber].proposalPassed);
			}}}

    function getProposal (uint _proposalNumber) constant returns (uint, string, uint, uint, address) {
		if (now > proposal[_proposalNumber].votingDeadline) {closeProposal(_proposalNumber);}
    	return (proposal[_proposalNumber].proposalGroup,
    			proposal[_proposalNumber].description,
    			proposal[_proposalNumber].quorumProposal,
    			proposal[_proposalNumber].votingDeadline,
				proposal[_proposalNumber].creator
    			);
    }

	function getIfVoted (uint _proposalNumber, address _member) constant returns (bool) {
		return (proposal[_proposalNumber].voters[msg.sender].alreadyVoted);
	}

    function getProposalVotes (uint _proposalNumber) constant returns (uint, int, bool, bool) {
    	return (proposal[_proposalNumber].numberOfVotes,
    			proposal[_proposalNumber].currentResult,
    			proposal[_proposalNumber].closed,
    			proposal[_proposalNumber].proposalPassed
    			);
    }

}

contract creditCommons {

        // @title creditCommons
        // @author Rogelio SEGOVIA; Matthew Slatter
		// @param sysAdmin is the system administrator address, the creator of the contract
        // @param baseUnits is the number of units before the comma
		address public sysAdmin;
		uint totalNrMembers;
		uint nrGroups;
		uint nrProposals;

	// @notice at creating the contract we declare the general variables
	function creditCommons() {
				// @param the initial sysAdmin is the address from which the contract is created
				sysAdmin = msg.sender;
			    totalNrMembers = 0;
			    nrGroups = 0;
			    nrProposals = 0;
        }

    function getTotals () constant returns (uint, uint, uint) {
    	return (totalNrMembers, nrGroups, nrProposals);
    }

	event NewGroup(address indexed _creator, uint indexed _groupIDN, string _groupNameN, uint _NGTimeStamp);
	event ModifyGroup (address indexed _modifier, uint indexed _groupIDM, string _groupNameM, uint _MGTimeStamp);
	event NewMember (address indexed _memberAddressN, string _memberAliasN, string _descriptionN, uint _NMTimeStamp);
	event JoinGroup (address _memberJG, string _aliasJG, uint _groupJG, string _groupNameJG, uint _JGTimeStamp);
	event ResignGroup (address _memberRG, string _aliasRG, uint _groupRG, string _groupNameRG, uint _RGTimeStamp);
	event Transaction (address indexed _sender, uint _senderAmount, address indexed _receiver, int _receiverAmount, uint _tTimeStamp);
    event ProposalAdded(uint proposalNumber, uint group, string description, address creator);
    event Voted(address voter, uint proposalNumber, int8 vote, int result);
    event ProposalResult(uint proposalNumber, int result, uint quorum, bool active);

	// @notice function to name a new sysAdmin
    function transferSysAdmin(address newSysAdmin) {
		if (msg.sender == sysAdmin) {
        sysAdmin = newSysAdmin;
		}
    }

	// @notice create a structure to file all members
	struct members {
		// @parameter key ID parameters
		bool isMember;
		string alias;
		string whisperID;
		string memberDescription;
		uint memberGroup;
		bool isIntertrade;
		bool isCommune;
		// @parameter balance is expressed in the member currency. Can only be modified by system operations
		int balance;
		uint mDebitLimit;
		uint mCreditLimit;
	}

	// @notice map the members structure into an array indexed by the members ethereum address
	mapping(address => members) member;

	// @notice create an index of members for listing purposes
	address[] memberIndex;

	// @notice anybody with an ethereum account can register in the system
	function registerSystem (string _alias, string _whisperID, string _description) {
		// @notice the caller provides a valid alias
		if (bytes(_alias).length != 0) {
		// @notice the caller is not already the system
			if (member[msg.sender].isMember != true) {
			member[msg.sender].isMember = true;
			member[msg.sender].alias = _alias;
			member[msg.sender].alias = _whisperID;
			member[msg.sender].memberDescription = _description;
			member[msg.sender].memberGroup = 0;
			member[msg.sender].isIntertrade = false;
			member[msg.sender].isCommune = false;
			member[msg.sender].balance = 0;
			member[msg.sender].mDebitLimit = 0;
			member[msg.sender].mCreditLimit = 0;
		NewMember (msg.sender, _alias, _description, now);
		memberIndex [memberIndex.length ++] = msg.sender;
		totalNrMembers = totalNrMembers + 1;
				}
			}
		}

	function modifyMember (string _alias, string _description) {
		if (bytes(_alias).length != 0) {member[msg.sender].alias = _alias;}
		if (bytes(_description).length != 0) {member[msg.sender].memberDescription = _description;}
		}

	function modifyMemberLimits (address _groupMember, uint _newDebitLimit, uint _newCreditLimit) {
		// @notice only the group commune can do
		if (msg.sender == group[member[msg.sender].memberGroup].commune) {
					member[_groupMember].mDebitLimit = _newDebitLimit;
					member[_groupMember].mCreditLimit = _newCreditLimit;
		}
	}

	// @notice anybody in the system can join a group
	function joinGroup (uint _groupJ) {
			// @notice the member is in the system
			if (member[msg.sender].isMember = true) {
				// @notice the group exists
				if (bytes(group[_groupJ].groupName).length != 0){
					// @notice if the group is open
					if (group[_groupJ].open = true) {
					// @notice the member is not in a group
					if (member[msg.sender].memberGroup == 0){
						makeMemberOfGroup (msg.sender, _groupJ);
					}
					}
					// @notice if the group is not open
					else {
                        newProposal (_groupJ, "new Member", member[msg.sender].memberDescription, 30, group[_groupJ].quorum);
					}
			}
		}
	}

	function acceptAtGroup ( address _newMember, uint _groupJ) {
		// @notice the commune can accept a new member in the group
		if (group[_groupJ].commune == msg.sender) {
			makeMemberOfGroup (_newMember, _groupJ);
		}
	}

	function makeMemberOfGroup (address _memberOfGroup, uint _group) internal {
		member[_memberOfGroup].memberGroup = _group;
		member[_memberOfGroup].balance = 0;
		member[_memberOfGroup].mDebitLimit = group[_group].gDebitLimit;
		member[_memberOfGroup].mCreditLimit = group[_group].gCreditLimit;
		group[_group].nrMembers = group[_group].nrMembers + 1;
		JoinGroup (_memberOfGroup, member[_memberOfGroup].alias, _group, group[_group].groupName, now);
	}

	function resignGroup () {
				uint _groupR = member[msg.sender].memberGroup;
				string _groupRN = group[_groupR].groupName;
				// @notice the account is not a group account
				if (group[_groupR].intertradeAccount != msg.sender) {
				// @notice the member is not commune of the group
				if (member[msg.sender].isCommune == false) {
					// @notice the member balance is zero
					if (member[msg.sender].balance == 0) {
						member[msg.sender].memberGroup = 0;
						member[msg.sender].mDebitLimit = 0;
						member[msg.sender].mCreditLimit = 0;
						group[_groupR].nrMembers = group[_groupR].nrMembers - 1;
						ResignGroup (msg.sender, member[msg.sender].alias, _groupR, _groupRN, now);
					}
				}
				}
			}

	function kickOffGroup (address _newMember) {

	}

	function getMember (address _memberG) constant returns (bool, string, string, uint, int, uint, uint) {
	    return (member[_memberG].isMember, member[_memberG].alias, member[_memberG].memberDescription, member[_memberG].memberGroup, member[_memberG].balance, member[_memberG].mDebitLimit, member[_memberG].mCreditLimit);
	}

	function getMemberStatus (address _memberG) constant returns (bool, bool) {
	   return (member[_memberG].isIntertrade, member[_memberG].isCommune);
	}

	function getMemberWhisper (address _memberG) constant returns (string) {
	   return (member[_memberG].whisperID);
	}

	function getNumberMembers () constant returns (uint _nrM) {
		_nrM = totalNrMembers;
	}

	function getMPbyIndex (uint _mIndex) constant returns (address _getMemberID) {
		if (_mIndex < memberIndex.length ++) {
		_getMemberID = memberIndex[_mIndex];
			}
	}

    // @notice create a structure to file all groups and their parameters
    struct groups {
    	string groupName;
    	string groupDescription;
    	string currencyName;
    	address intertradeAccount;
    	address commune;
    	// @parameter the exchange rate against the base currency is given in percentage (100 = 1/1)
    	uint rate;
    	uint gDebitLimit;
    	uint gCreditLimit;
    	bool open;
    	uint nrMembers;
    	uint quorum;
    }

    // @notice map the exchanges structure into an array indexed by a string (the string we use is the CES Exchange ID)
    mapping(uint => groups) group;

    // @notice create an index of exchanges for listing purposes
    uint[] groupIndex;

    // @notice A group can be created by any account in the system that is not in a group. A group is also an account and is identified by its account number. A new group therefore contains two accounts: its own, and its creators.
    function createGroup (string _groupName, string _description, string _currencyName, uint _rate, uint _debitLimit, uint _creditLimit, uint _intertradeDebitLimit, uint _intertradeCreditLimit, bool _open) {
    	// @notice the member exists in the system and the member is not in a group and the name is valid
    	if (member[msg.sender].isMember = true) {
    		if (member[msg.sender].memberGroup == 0) {
    				if (bytes(_groupName).length != 0) {
    					uint groupID = now;
    					group[groupID].groupName = _groupName;
    					group[groupID].currencyName = _currencyName;
    					group[groupID].intertradeAccount = msg.sender;
    					group[groupID].commune = msg.sender;
    					group[groupID].rate = _rate;
    					group[groupID].gDebitLimit = _debitLimit;
    					group[groupID].gCreditLimit = _creditLimit;
    					group[groupID].open = _open;
    					group[groupID].nrMembers = 1;
    					group[groupID].quorum = 3;
    						NewGroup(msg.sender, groupID, _groupName, now);
    						// @notice make the creator member of the group and set the group intertrade limits
    						member[msg.sender].memberGroup = groupID;
    						member[msg.sender].isIntertrade = true;
    						member[msg.sender].isCommune = true;
    						member[msg.sender].balance = 0;
    						member[msg.sender].mDebitLimit = _intertradeDebitLimit;
    						member[msg.sender].mCreditLimit = _intertradeCreditLimit;
    					groupIndex [groupIndex.length ++] = groupID;
    					nrGroups = nrGroups +1;
    				}
    			}
    	    }
    	}

    // @notice transfer group intertrade account. Old intertrade or sysAdmin can transfer group intertrade to another member of the group
    // @notice the reason to include sysAdmin is for the case the old commune disappears
    function transferGroupIntertrade (uint _groupID, address _newIntertrade) {
    	if ((msg.sender == group[_groupID].intertradeAccount) || (msg.sender == sysAdmin)) {
    		if (member[_newIntertrade].memberGroup == _groupID) {
        		member[group[_groupID].intertradeAccount].isIntertrade = false;
        		group[_groupID].intertradeAccount = _newIntertrade;
        		member[_newIntertrade].isIntertrade = true;
    			string _groupName = group[_groupID].groupName;
    			ModifyGroup (msg.sender, _groupID, _groupName, now);
    	}
    	}
    }

    // @notice transfer group commune. Old commune or sysAdmin can transfer group commune to another member of the group
    // @notice the reason to include sysAdmin is for the case the old commune disappears
    function transferGroupCommune (uint _groupID, address _newCommune) {
    	if ((msg.sender == group[_groupID].commune) || (msg.sender == sysAdmin)) {
    		if (member[_newCommune].memberGroup == _groupID) {
        		member[group[_groupID].commune].isCommune = false;
        		group[_groupID].commune = _newCommune;
        		member[_newCommune].isCommune = true;
    			string _groupName = group[_groupID].groupName;
    			ModifyGroup (msg.sender, _groupID, _groupName, now);
    	}
    	}
    }

    // @notice the commune can modify one, several or all parameters of a group. If one parameter is left empty, it remains the same. Only the exchange commune can change its parameters
    function modifyGroup (uint _groupID, string _groupName, string _description, string _currencyName, uint _rate, uint _debitLimit, uint _creditLimit, uint _intertradeDebitLimit, uint _intertradeCreditLimit, bool _open, uint _newQuorum) {
    	        if (msg.sender == group[_groupID].commune) {
    			// @notice if a value for a parameter is given, change the parameter, if empty retain old value
    			if (bytes(_groupName).length != 0) {group[_groupID].groupName = _groupName;}
    			if (bytes(_description).length != 0) {group[_groupID].groupDescription = _description;}
    			if (bytes(_currencyName).length != 0) {group[_groupID].currencyName = _currencyName;}
    			if (_rate != 0) {group[_groupID].rate = _rate;}
    			if (_debitLimit != 0) {group[_groupID].gDebitLimit = _debitLimit;}
    			if (_creditLimit != 0) {group[_groupID].gCreditLimit = _creditLimit;}
    			if (_intertradeDebitLimit != 0) {member[group[_groupID].intertradeAccount].mDebitLimit = _intertradeDebitLimit;}
    			if (_intertradeCreditLimit != 0) {member[group[_groupID].intertradeAccount].mCreditLimit = _intertradeCreditLimit;}
    			if (_open == true) {group[_groupID].open = true;}
    			if (_newQuorum != 0) {group[_groupID].quorum = _newQuorum;}
    			ModifyGroup (msg.sender, _groupID, _groupName, now);
    				}

    }

    function getGroupDescription (uint _groupG) constant returns (string, string, string, bool, uint) {
    return (group[_groupG].groupName, group[_groupG].groupDescription, group[_groupG].currencyName, group[_groupG].open, group[_groupG].nrMembers);
    }

    function getGroupRates (uint _groupG) constant returns (uint, uint, uint) {
    return (group[_groupG].rate, group[_groupG].gDebitLimit, group[_groupG].gCreditLimit);
    }

    function getGroupManagement (uint _groupG) constant returns (address, address, uint, uint) {
    return (group[_groupG].intertradeAccount, group[_groupG].commune, member[group[_groupG].intertradeAccount].mDebitLimit, member[group[_groupG].intertradeAccount].mCreditLimit);
    }

    function getNumberGroups () constant returns (uint _nrG) {
        _nrG = nrGroups;
    }

    function getGroupbyIndex (uint _gIndex) constant returns (uint _getGroupID) {
    	if (_gIndex < groupIndex.length ++) {
    	_getGroupID = groupIndex[_gIndex];
    		}
    }

	// @notice function transfer form the member of the same exchange or to the member of another exchange. The amount is expressed in the sender currency
	function transfer (address _to, uint _fromAmount) {
		// @notice the given amount is converted to integer in order to work with only integers
		int _intFromAmount = int (_fromAmount);
		address _fromGroupAccount = group[member[msg.sender].memberGroup].intertradeAccount;
		address _toGroupAccount = group[member[_to].memberGroup].intertradeAccount;
		int _intFromDLimit = - int(member[msg.sender].mDebitLimit);
		int _intToCLimit = int(member[msg.sender].mCreditLimit);
		// @notice conversions if the transaction is within groups
		if (_fromGroupAccount != _toGroupAccount) {
			// @the amount is converted to the receiver currency
			uint _rateSenderU = group[member[msg.sender].memberGroup].rate;
			uint _rateReceiverU = group[member[_to].memberGroup].rate;
			int _rateSender = int(_rateSenderU);
			int _rateReceiver = int(_rateReceiverU);
			int _toAmount = _intFromAmount * _rateSender/ _rateReceiver;
		}
		// @notice if the limits are not surpassed, we proceed with the transfer
		if ((member[msg.sender].balance - _intFromAmount) > _intFromDLimit) {
			if ((member[_to].balance + _toAmount) < _intToCLimit) {
				member[msg.sender].balance -= _intFromAmount;
				member[_fromGroupAccount].balance -= _intFromAmount;
				member[_to].balance += _toAmount;
				member[_toGroupAccount].balance += _toAmount;
			}
		}
			Transaction (msg.sender, _fromAmount, _to, _toAmount, now);
	}

    struct Proposals {
		address creator;
    	uint proposalGroup;
    	string title;
        string description;
        uint votingDeadline;
        uint quorumProposal;
        bool closed;
        bool proposalPassed;
        uint numberOfVotes;
        int currentResult;
		mapping (address => Voters) voters;
    }

	struct Voters {
		bool alreadyVoted;

	}

	mapping (uint => Proposals) proposal;

	// @ notice Function to create a new proposal
    function newProposal (uint _proposalGroup, string _title, string _description, uint _days, uint _quorum) {
        nrProposals ++;
        uint proposalNumber = nrProposals;
		proposal[proposalNumber].creator = msg.sender;
        proposal[proposalNumber].proposalGroup = _proposalGroup;
        proposal[proposalNumber].title = _title;
        proposal[proposalNumber].description = _description;
        proposal[proposalNumber].votingDeadline = now + _days * 1 days;
        proposal[proposalNumber].quorumProposal = _quorum;
        proposal[proposalNumber].closed = false;
        proposal[proposalNumber].proposalPassed = false;
        proposal[proposalNumber].numberOfVotes = 0;
        proposal[proposalNumber].currentResult = 0;
		proposal[proposalNumber].voters[msg.sender].alreadyVoted = false;
        ProposalAdded(proposalNumber, _proposalGroup, _description, msg.sender);
    }

    function vote(uint _proposalNumber, int8 _choice) {
		if (now > proposal[_proposalNumber].votingDeadline) {closeProposal(_proposalNumber);}
		if (proposal[_proposalNumber].closed == false) {
		if (member[msg.sender].memberGroup == proposal[_proposalNumber].proposalGroup) {
					if (proposal[_proposalNumber].voters[msg.sender].alreadyVoted == false) {
        				proposal[_proposalNumber].numberOfVotes += 1;
						proposal[_proposalNumber].voters[msg.sender].alreadyVoted = true;
			if (_choice == 1) {proposal[_proposalNumber].currentResult += 1; }
			if (_choice == -1) {proposal[_proposalNumber].currentResult -= 1; }
        // Create a log of this event
        Voted(msg.sender, _proposalNumber, _choice, proposal[_proposalNumber].currentResult);
					}
					}
				}
			}

    function closeProposal(uint _proposalNumber) {
        /* If difference between support and opposition is larger than margin */
		if (now > proposal[_proposalNumber].votingDeadline) {
		if (proposal[_proposalNumber].closed == false) {
        if ((proposal[_proposalNumber].numberOfVotes > proposal[_proposalNumber].quorumProposal)
        		|| (proposal[_proposalNumber].currentResult > 0))
        {
            proposal[_proposalNumber].proposalPassed = true;
        } else {
            proposal[_proposalNumber].proposalPassed = false;
        }
        // Fire Events
        ProposalResult(_proposalNumber, proposal[_proposalNumber].currentResult, proposal[_proposalNumber].numberOfVotes, proposal[_proposalNumber].proposalPassed);
			}}}

    function getProposal (uint _proposalNumber) constant returns (uint, string, uint, uint, address) {
		if (now > proposal[_proposalNumber].votingDeadline) {closeProposal(_proposalNumber);}
    	return (proposal[_proposalNumber].proposalGroup,
    			proposal[_proposalNumber].description,
    			proposal[_proposalNumber].quorumProposal,
    			proposal[_proposalNumber].votingDeadline,
				proposal[_proposalNumber].creator
    			);
    }

	function getIfVoted (uint _proposalNumber, address _member) constant returns (bool) {
		return (proposal[_proposalNumber].voters[msg.sender].alreadyVoted);
	}

    function getProposalVotes (uint _proposalNumber) constant returns (uint, int, bool, bool) {
    	return (proposal[_proposalNumber].numberOfVotes,
    			proposal[_proposalNumber].currentResult,
    			proposal[_proposalNumber].closed,
    			proposal[_proposalNumber].proposalPassed
    			);
    }

}


implement add with renewals

implement add with renewals

pragma solidity ^0.4.11;
import "./StandardToken.sol";
import "./PLCRVoting.sol";

/*
=======
 TO DO
=======

implement events
refactor & wrap check & transfer

*/

contract Registry {

    /*
     * Storage
     */

    //Registry storage
    StandardToken public token;
    PLCRVoting public voting;
    mapping(bytes32 => Publisher) public whitelist; //domainHash => Publisher struct
    mapping(bytes32 => Application) public appPool; //holds applications for both domain and parameter appPool
    mapping(bytes32 => Params) public paramSnapshots;
    mapping(uint => VoteInfo) public pollInfo; // holds information on individual polls
    mapping(address => mapping(uint => bool)) public voterInfo; // holds information on voters' token claims
    string domain;
    //Parameter storage
    mapping(bytes32 => uint) public Parameters;
    mapping(uint => bytes32) public idToHash;
    string parameter;
    uint value;

    struct Publisher {
        address owner;
        uint expTime;
        uint deposit;

        uint nextExpTime //
        uint prevDeposit // total withdrawable amount
        uint nextDeposit //
        bool renewal;
    }

    struct Application {
        address owner;
        bool challenged;
        uint challengeTime; //End of challenge period
        address challenger;

        string parameter;
        uint value;
    }

    struct Params {
        // parameters concerning the whitelist and application pool
        uint minDeposit;
        uint challengeLen;
        uint registryLen;

        // parameters to be passed into the voting contract
        uint commitVoteLen;
        uint revealVoteLen;
        uint majority;

        // parameter representing the scale of how token rewards are distributed
        uint dispensationPct;
    }

    struct VoteInfo {
        // if the vote has been processed
        bool processed;
        // amount of leftover tokens available for winning party to claim
        // (a byproduct of needing to floor the distributed tokens to voters)
        uint256 remainder;
        address claimer;
    }

    // constant used to help represent doubles as ints
    uint256 constant private MULTIPLIER = 10 ** 18;

    /*
     * Constructor
     */
    /// @param _minDeposit      application & challenger deposit amounts
    /// @param _challengeLen    duration of the challenge period
    /// @param _registryLen     duration of a registration’s validity
    /// @param _commitVoteLen   duration of the commit period in token votes
    /// @param _revealVoteLen   duration of reveal period in token votes
    /// @param _dispensationPct percentage of forfeited deposit distributed to winning party; uint between 0 and 100
    /// @param _majority        percentage of votes that constitutes the majority; uint between 0 and 100

    function Registry(address _token,
       uint _minDeposit,
       uint _challengeLen,
       uint _registryLen,
       uint _commitVoteLen,
       uint _revealVoteLen,
       uint _dispensationPct,
       uint _majority) {

       token = StandardToken(_token);
       // initialize values
       Parameters[sha3("minDeposit")]        = _minDeposit;
       Parameters[sha3("challengeLen")]      = _challengeLen;
       Parameters[sha3("registryLen")]       = _registryLen;
       Parameters[sha3("commitVoteLen")]     = _commitVoteLen;
       Parameters[sha3("revealVoteLen")]     = _revealVoteLen;
       Parameters[sha3("dispensationPct")]   = _dispensationPct;
       Parameters[sha3("majority")]          = _majority;
    }

    // called by an applicant to apply (moves them into the application pool on success)
    function apply(string _domain) public {
        bytes32 domainHash = sha3(_domain);
        // must be a new member of the whitelist
        require(whitelist[domainHash].owner == 0);
        // initialize with the current values of all parameters
        initializeSnapshot(domainHash);
        initApplication(domainHash, msg.sender);
        appPool[domainHash].domain = _domain;
    }

    // helper function to apply() and proposeUpdate()
    // initialize general application
    function initApplication(bytes32 _hash, address _applicant) private {
        // applicant must pay the current value of minDeposit
        uint deposit = paramSnapshots[_hash].minDeposit;
        // check to prevent repeat applications
        require(appPool[_hash].owner == 0);
        // check that registry can take sufficient amount of tokens from the applicant
        require(token.allowance(_applicant, this) >= deposit);
        token.transferFrom(_applicant, this, deposit);
        appPool[_hash].challengeTime = now + paramSnapshots[_hash].challengeLen;
        appPool[_hash].owner = _applicant;
    }

    function renew (string _domain) {
        bytes32 domainHash = sha3(_domain);
        // check if no active renewal
        require(hasRenewal(domainHash) == false);
        require(msg.sender == whitelist[domainHash].owner); // checks that you are the owner of the domain
        require(appPool[domainHash].owner == 0); // no double renewal
        uint deposit = get('minDeposit');
        //Check if existing deposit is sufficient
        if (whitelist[domainHash].deposit + whitelist[domainHash].prevDeposit >= deposit){
            // only take from deposit
            if (whitelist[domainHash].deposit >= deposit)
            {
                uint difference = whitelist[domainHash].deposit - deposit;
                whitelist[domainHash].deposit = difference;
            }
            // take whole deposit and part of prevDeposit
            else
            {
                // take all of locked deposit and a portion of unlocked deposit
                uint difference = deposit - whitelist[domainHash].deposit;
                uint difference2 = whitelist[domainHash].prevDeposit - difference;
                whitelist[domainHash].prevDeposit = difference2;
            }
        }
        //if insufficient # of tokens, then must send in the difference
        else {
            uint difference = deposit-(whitelist[domainHash].deposit + whitelist.[domainHash].prevDeposit);
            require(token.allowance(msg.sender, this) >= difference);
            token.transferFrom(msg.sender, this, difference);
            whitelist[domainHash].deposit = 0;
            whitelist[domainHash].prevDeposit = 0;
        }
        //apply
        initializeSnapshot(_domain);
        appPool[domainHash].challengeTime = now + paramSnapshots[_hash].challengeLen;
        appPool[domainHash].owner = msg.sender;
        whitelist[domainHash].renewal = true;
    }

    function claimDeposit(string _domain, uint _amount) public {
        bytes32 domainHash = sha3(_domain);
        require(msg.sender == whitelist[domainHash].owner);
        uint difference = whitelist[domainHash].prevDeposit - _amount;
        require(difference >= 0);
        token.transfer(msg.sender, _amount);
        whitelist[domainHash].prevDeposit = difference;
    }

    // called by domain owner to activate renewal period and allow additional renewals
    function activateRenewal(string _domain) public {
        bytes32 _hash = sha3(_domain);
        if (hasRenewal(_hash) && whitelist[_hash].expTime <= now)
        {
            whitelist[_hash].expTime = whitelist[_hash].nextExpTime;
            whitelist[_hash].prevDeposit += whitelist[_hash].deposit;
            whitelist[_hash].deposit = whitelist[_hash].nextDeposit;
            whitelist[_hash].renewal = false;
        }
    }

    // called by any adtoken holder to challenge an application to the whitelist
    // initialize vote to accept/reject a domain to the registry
    function challengeApplication(string _domain) public returns(pollID) {
        bytes32 domainHash = sha3(_domain);
        challenge(domainHash, msg.sender);
        // start a vote
        uint pollID = callVote(_domain
        ,paramSnapshots[domainHash].majority
        ,paramSnapshots[domainHash].commitVoteLen
        ,paramSnapshots[domainHash].revealVoteLen);
        idToHash[pollID] = domainHash;
        return pollID;
    }

    // helper function to challengeApplication() and challengeProposal()
    function challenge(bytes32 _hash, address _challenger) private {
        // check that registry can take sufficient amount of tokens from the challenger
        uint deposit = paramSnapshots[_hash].minDeposit;
        require(token.allowance(_challenger, this) >= deposit);
        token.transferFrom(_challenger, this, deposit);

        // prevent someone from challenging an unintialized application, rechallenging,
        // or challenging after the challenge period has ended
        require(appPool[_hash].owner != 0);
        require(appPool[_hash].challenged == false);
        require(appPool[_hash].challengeTime > now);

        // update the application's status
        appPool[_hash].challenged = true;
        appPool[_hash].challenger = _challenger;
    }

    // helper function to the challenge() function. Initializes a vote through the voting contract
    // returns a poll id
    function callVote(string _proposalString,
        uint _majority,
        uint _commitVoteLen,
        uint _revealVoteLen
        ) private returns (uint) {
        // event that vote has started
        uint pollID = voting.startPoll( _proposalString, _majority, _commitVoteLen,  _revealVoteLen);
        return pollID;
    }

    // a one-time function for each completed vote
    // if domain won: domain is moved to the whitelist and applicant is rewarded tokens, return true
    // if domain lost: challenger is rewarded tokens, return false
    function processResult(uint _pollID) returns(bool)
    {
        bytes32 domainHash = idToHash[_pollID];
        require(isDomainApp(domainHash));  // processing parameter hash this way is unintended behavior
        require(pollInfo[_pollID].processed == false);
        // ensures the result cannot be processed again
        pollInfo[_pollID].processed = true;

        if (voting.isPassed(_pollID)) {
            // add to registry
            add(domainHash, appPool[domainHash].owner);
            pollInfo[_pollID].claimer = appPool[domainHash].owner;
            // give tokens to applicant based on dist and total tokens
            giveWinnerReward(domainHash, appPool[domainHash].owner);
            // uninitialize application
            delete appPool[domainHash].owner;
            return true;
        }
        else {
            pollInfo[_pollID].claimer = appPool[domainHash].challenger;
            giveWinnerReward(domainHash, appPool[domainHash].challenger);
            token.transfer(appPool[domainHash].challenger, paramSnapshots[domainHash].minDeposit);
            delete appPool[domainHash].owner;
            return false;
        }
    }

    // internal function to give applicant/challenger reward
    // if dispensationPct does not divide minDeposit evenly, gives then the extra token
    function giveWinnerReward(_hash, _address) private {
        uint256 minDeposit = paramSnapshots[_hash].minDeposit;
        uint256 dispensationPct = paramSnapshots[_hash].dispensationPct;
        uint256 rewardTokens = minDeposit * (dispensationPct) / 100;
        if ((minDeposit * dispensationPct) % 100 != 0) {
            rewardTokens++;
        }
        token.transfer(_address, rewardTokens);
    }

    // called by each voter to claim their reward for each completed vote
    function claimReward(uint _pollID, uint _salt) {
        // checks if a voter has claimed tokens
        require(voterInfo[msg.sender][_pollID] == false);
        uint reward = giveTokens(_pollID, _salt, msg.sender);
        // ensures a voter cannot claim tokens again
        token.transfer(msg.sender, reward);
        voterInfo[msg.sender][_pollID] = true;
    }

    // number of tokens person used to vote / total number of tokens for winning side
    // scale using distribution number, give the tokens
    function giveTokens(uint _pollID, uint _salt, address _voter) private returns(uint) {
        bytes32 hash = idToHash[_pollID];
        uint256 minDeposit = paramSnapshots[hash].minDeposit;
        uint256 dispensationPct = paramSnapshots[hash].dispensationPct;
        uint256 totalTokens = voting.getTotalNumberOfTokensForWinningOption(_pollID);
        uint256 voterTokens = voting.getNumPassingTokens(_pollID, _salt, _voter);

        uint256 rewardTokens = minDeposit * (100 - dispensationPct) / 100;
        uint256 numerator = voterTokens * rewardTokens * MULTIPLIER;
        uint256 denominator = totalTokens * MULTIPLIER;
        uint256 remainder = numerator % denominator;

        // save remainder tokens in the form of decimal numbers with 18 places represented
        // as a uint256
        pollInfo[_pollID].remainder += remainder;

        return numerator / denominator;
    }

    function claimExtraReward(uint _pollID) {
        uint256 reward = pollInfo[_pollID].remainder / MULTIPLIER;
        pollInfo[_pollID].remainder = pollInfo[_pollID].remainder - reward * MULTIPLIER;
        token.transfer(pollInfo[_pollID].claimer, reward);
    }

    // called to move an applying domain to the whitelist
    // iff the domain's challenge period has passed without a challenge
    function moveToRegistry(string _domain) public {
        bytes32 domainHash = sha3(_domain);
        require(appPool[domainHash].challengeTime < now);
        require(appPool[domainHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[domainHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        add(domainHash, appPool[domainHash].owner);
        delete appPool[domainHash].owner;
    }

    // ISSUE WITH OVERLAP

    // private function to add a domain name to the whitelist
    function add(bytes32 _domainHash, address _owner) private {
        uint expiration = paramSnapshots[_domainHash].registryLen;
        if (whitelist[_domainHash].renewal = true)
        {
            whitelist[_domainHash].nextExpTime = whitelist[_domainHash].expTime + expiration;
            whitelist[_domainHash].nextDeposit = paramSnapshots[_domainHash].minDeposit;
        }
        else
        {
            whitelist[_domainHash].expTime = now + expiration;
            whitelist[_domainHash].deposit = paramSnapshots[_domainHash].minDeposit;
        }
        whitelist[_domainHash].owner = _owner;
    }

    /*
     * Helper Functions
     */

    // STATIC

    // returns true if a renewal has been initialized
    function hasRenewal(bytes32 _hash) private constant returns (bool) {
        return whitelist[_hash].renewal;
    }

    //returns true if Application is for domain and not parameter
    function isDomainApp(bytes32 _hash) private constant returns(bool){
        return appPool[_hash].parameter == "";  // checks if param string is initialized
    }

    // checks if a domain name is in the whitelist and unexpired
    function isVerified(string _domain) public constant returns (bool) {
        bytes32 domainHash = sha3(_domain);
        return whitelist[domainHash].expTime > now;
    }

    // DYNAMIC

    // private function to initialize a snapshot of parameters for each application
    function initializeSnapshot(bytes32 _hash) private {
        initializeSnapshotParam(_hash);  // maybe put the two together
        paramSnapshots[_hash].registryLen = get("registryLen");
    }

/*****************************************************************************/

    //called by a user who wishes to change a parameter
    //initializes a proposal to change a parameter
    function proposeUpdate(string _parameter, uint _value) public {
        require(_parameter != "");
        bytes32 parameterHash = sha3(_parameter, _value);
        // initialize application with a with the current values of all parameters
        initializeSnapshotParam(parameterHash);
        initApplication(parameterHash, msg.sender);
        appPool[parameterHash].parameter = _parameter;
        appPool[parameterHash].value = _value;
    }

    //called by user who wishes to reject a proposal
    //initializes a vote to accept/reject the param change proposal
    function challengeProposal(string _parameter, uint _value) public {
        bytes32 parameterHash = sha3(_parameter, _value);
        challenge(parameterHash, msg.sender);
        // start a vote
        uint pollID = callVote(_parameter
        ,paramSnapshots[parameterHash].majority
        ,paramSnapshots[parameterHash].commitVoteLen
        ,paramSnapshots[parameterHash].revealVoteLen);
        idToHash[pollID] = parameterHash;
    }

    // called to change parameter
    // iff the proposal's challenge period has passed without a challenge
    function setParams(string _parameter, uint _value) public {
        bytes32 parameterHash = sha3(_parameter, _value);
        require(appPool[parameterHash].challengeTime < now);
        require(appPool[parameterHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[parameterHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        Parameters[sha3(_parameter)] = _value;
        delete appPool[parameterHash].owner;
    }

    // a one-time function for each completed vote
    // if proposal won: new parameter value is set, and applicant is rewarded tokens, return true
    // if prospsal lost: challenger is rewarded tokens, return false
    function processProposal(uint _pollID) returns(bool)
    {
        require(pollInfo[_pollID].processed == false);
        bytes32 parameterHash = idToHash[_pollID];
        parameter = appPool[parameterHash].parameter;
        value = appPool[parameterHash].value;
        delete appPool[parameterHash].owner;

        if (voting.isPassed(_pollID)) {
            pollInfo[_pollID].claimer = appPool[parameterHash].owner;
            // setting the value of parameter
            Parameters[sha3(parameter)] = value;
            // give tokens to applicant based on dist and total tokens IMPLEMENT
            giveWinnerReward(parameterHash, appPool[parameterHash].owner);
            token.transfer(appPool[parameterHash].owner, paramSnapshots[parameterHash].minDeposit);
            return true;
        }
        else {
            pollInfo[_pollID].claimer = appPool[parameterHash].challenger;
            // give tokens to challenger based on dist and total tokens
            giveWinnerReward(parameterHash, appPool[parameterHash].challenger);
            token.transfer(appPool[parameterHash].challenger, paramSnapshots[parameterHash].minDeposit);
            return false;
        }
        // ensures the result cannot be processed again
        pollInfo[_pollID].processed = true;
    }

     // private function to initialize a snapshot of parameters for each proposal
     function initializeSnapshotParam(bytes32 _hash) private {
        paramSnapshots[_hash].minDeposit = get("minDeposit");
        paramSnapshots[_hash].challengeLen = get("challengeLen");
        paramSnapshots[_hash].commitVoteLen = get("commitVoteLen");
        paramSnapshots[_hash].revealVoteLen = get("revealVoteLen");
        paramSnapshots[_hash].majority = get("majority");
        paramSnapshots[_hash].dispensationPct = get("dispensationPct");
    }

    // interface for retrieving config parameter from hashmapping
    /// @param _keyword key for hashmap (only useful when keyword matches variable name)
    function get(string _keyword) public constant returns (uint) {
       return Parameters[sha3(_keyword)];
    }

/*****************************************************************************/

    // Helper Functions

}

pragma solidity ^0.4.11;
import "./StandardToken.sol";
import "./PLCRVoting.sol";

/*
=======
 TO DO
=======

implement events
refactor & wrap check & transfer
check for unintended behavior when domain functions are called with param hashes and vice versa (via pollID)
make sure people can't create random parameters

*/

contract Registry {

    /*
     * Storage
     */

    //Registry storage
    StandardToken public token;
    PLCRVoting public voting;
    mapping(bytes32 => Publisher) public whitelist; //domainHash => Publisher struct
    mapping(bytes32 => Application) public appPool; //holds applications for both domain and parameter appPool
    mapping(bytes32 => Params) public paramSnapshots;
    mapping(uint => VoteInfo) public pollInfo; // holds information on individual polls
    mapping(address => mapping(uint => bool)) public voterInfo; // holds information on voters' token claims
    string domain;
    //Parameter storage
    mapping(bytes32 => uint) public Parameters;
    mapping(uint => bytes32) public idToHash;
    string parameter;
    uint value;

    struct Publisher {
        address owner;
        uint expTime;
        uint deposit;
        uint nextExpTime //
        uint prevDeposit // total withdrawable amount
        uint nextDeposit //
    }

    struct Application {
        address owner;
        bool challenged;
        uint challengeTime; //End of challenge period
        address challenger;

        string parameter;
        uint value;
    }

    struct Params {
        // parameters concerning the whitelist and application pool
        uint minDeposit;
        uint challengeLen;
        uint registryLen;

        // parameters to be passed into the voting contract
        uint commitVoteLen;
        uint revealVoteLen;
        uint majority;

        // parameter representing the scale of how token rewards are distributed
        uint dispensationPct;
    }

    struct VoteInfo {
        // if the vote has been processed
        bool processed;
        // amount of leftover tokens available for winning party to claim
        // (a byproduct of needing to floor the distributed tokens to voters)
        uint256 remainder;
        address claimer;
    }

    // constant used to help represent doubles as ints
    uint256 constant private MULTIPLIER = 10 ** 18;

    /*
     * Constructor
     */
    /// @param _minDeposit      application & challenger deposit amounts
    /// @param _challengeLen    duration of the challenge period
    /// @param _registryLen     duration of a registration’s validity
    /// @param _commitVoteLen   duration of the commit period in token votes
    /// @param _revealVoteLen   duration of reveal period in token votes
    /// @param _dispensationPct percentage of forfeited deposit distributed to winning party; uint between 0 and 100
    /// @param _majority        percentage of votes that constitutes the majority; uint between 0 and 100

    function Registry(address _token,
       uint _minDeposit,
       uint _challengeLen,
       uint _registryLen,
       uint _commitVoteLen,
       uint _revealVoteLen,
       uint _dispensationPct,
       uint _majority) {

       token = StandardToken(_token);
       // initialize values
       Parameters[sha3("minDeposit")]        = _minDeposit;
       Parameters[sha3("challengeLen")]      = _challengeLen;
       Parameters[sha3("registryLen")]       = _registryLen;
       Parameters[sha3("commitVoteLen")]     = _commitVoteLen;
       Parameters[sha3("revealVoteLen")]     = _revealVoteLen;
       Parameters[sha3("dispensationPct")]   = _dispensationPct;
       Parameters[sha3("majority")]          = _majority;
    }

    // called by an applicant to apply (moves them into the application pool on success)
    function apply(string _domain) public {
        bytes32 domainHash = sha3(_domain);
        require(hasRenewal[domainHash] == false);
        // initialize with the current values of all parameters
        initializeSnapshot(domainHash);
        initApplication(domainHash, msg.sender);
        appPool[domainHash].domain = _domain;
    }

    // helper function to apply() and proposeUpdate()
    // initialize general application
    function initApplication(bytes32 _hash, address _applicant) private {
        // applicant must pay the current value of minDeposit
        uint deposit = paramSnapshots[_hash].minDeposit;
        // check to prevent repeat applications
        require(appPool[_hash].owner == 0);
        // check that registry can take sufficient amount of tokens from the applicant
        require(token.allowance(_applicant, this) >= deposit);
        token.transferFrom(_applicant, this, deposit);
        appPool[_hash].challengeTime = now + paramSnapshots[_hash].challengeLen;
        appPool[_hash].owner = _applicant;
    }

    function renew (string _domain) {
        bytes32 domainHash = sha3(_domain);
        // check if no active renewal
        require(hasRenewal(domainHash) == false);
        require(msg.sender == whitelist[domainHash].owner); // checks that you are the owner of the domain
        require(appPool[_hash].owner == 0); // no double renewal
        uint deposit = get('minDeposit');
        //Check if existing deposit is sufficient
        if (whitelist[domainHash].deposit + whitelist[domainHash].prevDeposit >= deposit){
            //apply
            initializeSnapshot(_domain);
            appPool[_hash].challengeTime = now + paramSnapshots[_hash].challengeLen;
            appPool[_hash].owner = msg.sender;
            // only take from deposit
            if (whitelist[domainHash].deposit >= deposit)
            {
                uint difference = whitelist[domainHash].deposit - deposit;
                whitelist[domainHash].deposit = difference;
            }
            // take whole deposit and part of prevDeposit
            else
            {
                // take all of locked deposit and a portion of unlocked deposit
                uint difference = deposit - whitelist[domainHash].deposit;
                uint difference2 = whitelist[domainHash].prevDeposit - difference;
                whitelist[domainHash].prevDeposit = difference2;
            }

        }
        //if insufficient # of tokens, then must send in the difference
        else {
            uint difference = deposit-(whitelist[domainHash].deposit + whitelist.[domainHash].prevDeposit);
            require(token.allowance(msg.sender, this) >= difference);
            token.transferFrom(msg.sender, this, difference);
            whitelist[domainHash].deposit = 0;
            whitelist[domainHash].prevDeposit = 0;
            //apply
            initializeSnapshot(_domain);
            appPool[_hash].challengeTime = now + paramSnapshots[_hash].challengeLen;
            appPool[_hash].owner = msg.sender;
        }
    }

    function claimDeposit(string _domain, uint _amount) public {
        bytes32 domainHash = sha3(_domain);
        require(msg.sender == whitelist[domainHash].owner);
        uint difference = whitelist[domainHash].prevDeposit - _amount;
        require(difference >= 0);
        token.transfer(msg.sender, _amount);
        whitelist[domainHash].prevDeposit = difference;
    }

    // checks to see if a renewal is there
    function hasRenewal(bytes32 _hash) private returns (bool) {
        return whitelist[_hash].nextExpTime > whitelist[_hash].expTime;
    }

    // called by domain owner to activate renewal period and allow additional renewals
    function activateRenewal(string _domain) public {
        bytes32 _hash = sha3(_domain);
        if (hasRenewal(_hash) && whitelist[_hash].expTime <= now)
        {
            whitelist[_hash].expTime = whitelist[_hash].nextExpTime;
            whitelist[_hash].prevDeposit += whitelist[_hash].deposit;
            whitelist[_hash].deposit = whitelist[_hash].nextDeposit;
        }
    }

    function isDomainApp(bytes32 _hash) private {
        return appPool[_hash].parameter == "";  // checks if param string is initialized
    }

    // called by any adtoken holder to challenge an application to the whitelist
    // initialize vote to accept/reject a domain to the registry
    function challengeApplication(string _domain) public returns(pollID) {
        bytes32 domainHash = sha3(_domain);
        challenge(domainHash, msg.sender);
        // start a vote
        uint pollID = callVote(_domain
        ,paramSnapshots[domainHash].majority
        ,paramSnapshots[domainHash].commitVoteLen
        ,paramSnapshots[domainHash].revealVoteLen);
        idToHash[pollID] = domainHash;
        return pollID;
    }

    // helper function to challengeApplication() and challengeProposal()
    function challenge(bytes32 _hash, address _challenger) private {
        // check that registry can take sufficient amount of tokens from the challenger
        uint deposit = paramSnapshots[_hash].minDeposit;
        require(token.allowance(_challenger, this) >= deposit);
        token.transferFrom(_challenger, this, deposit);

        // prevent someone from challenging an unintialized application, rechallenging,
        // or challenging after the challenge period has ended
        require(appPool[_hash].owner != 0);
        require(appPool[_hash].challenged == false);
        require(appPool[_hash].challengeTime > now);

        // update the application's status
        appPool[_hash].challenged = true;
        appPool[_hash].challenger = _challenger;
    }

    // helper function to the challenge() function. Initializes a vote through the voting contract
    // returns a poll id
    function callVote(string _proposalString,
        uint _majority,
        uint _commitVoteLen,
        uint _revealVoteLen
        ) private returns (uint) {
        // event that vote has started
        uint pollID = voting.startPoll( _proposalString, _majority, _commitVoteLen,  _revealVoteLen);
        return pollID;
    }

    // a one-time function for each completed vote
    // if domain won: domain is moved to the whitelist and applicant is rewarded tokens, return true
    // if domain lost: challenger is rewarded tokens, return false
    function processResult(uint _pollID) returns(bool)
    {
        bytes32 domainHash = idToHash[_pollID];
        require(isDomainApp(domainHash));  // processing parameter hash this way is unintended behavior
        require(pollInfo[_pollID].processed == false);
        // ensures the result cannot be processed again
        pollInfo[_pollID].processed = true;

        if (voting.isPassed(_pollID)) {
            // add to registry
            add(domainHash, appPool[domainHash].owner);
            pollInfo[_pollID].claimer = appPool[domainHash].owner;
            // give tokens to applicant based on dist and total tokens
            giveWinnerReward(domainHash, appPool[domainHash].owner);
            // uninitialize application
            delete appPool[domainHash].owner;
            return true;
        }
        else {
            pollInfo[_pollID].claimer = appPool[domainHash].challenger;
            giveWinnerReward(domainHash, appPool[domainHash].challenger);
            token.transfer(appPool[domainHash].challenger, paramSnapshots[domainHash].minDeposit);
            delete appPool[domainHash].owner;
            return false;
        }
    }

    // internal function to give applicant/challenger reward
    // if dispensationPct does not divide minDeposit evenly, gives then the extra token
    function giveWinnerReward(_hash, _address) private {
        uint256 minDeposit = paramSnapshots[_hash].minDeposit;
        uint256 dispensationPct = paramSnapshots[_hash].dispensationPct;
        uint256 rewardTokens = minDeposit * (dispensationPct) / 100;
        if ((minDeposit * dispensationPct) % 100 != 0) {
            rewardTokens++;
        }
        token.transfer(_address, rewardTokens);
    }

    // called by each voter to claim their reward for each completed vote
    function claimReward(uint _pollID, uint _salt) {
        // checks if a voter has claimed tokens
        require(voterInfo[msg.sender][_pollID] == false);
        uint reward = giveTokens(_pollID, _salt, msg.sender);
        // ensures a voter cannot claim tokens again
        token.transfer(msg.sender, reward);
        voterInfo[msg.sender][_pollID] = true;
    }

    // number of tokens person used to vote / total number of tokens for winning side
    // scale using distribution number, give the tokens
    function giveTokens(uint _pollID, uint _salt, address _voter) private returns(uint) {
        bytes32 hash = idToHash[_pollID];
        uint256 minDeposit = paramSnapshots[hash].minDeposit;
        uint256 dispensationPct = paramSnapshots[hash].dispensationPct;
        uint256 totalTokens = voting.getTotalNumberOfTokensForWinningOption(_pollID);
        uint256 voterTokens = voting.getNumPassingTokens(_pollID, _salt, _voter);

        uint256 rewardTokens = minDeposit * (100 - dispensationPct) / 100;
        uint256 numerator = voterTokens * rewardTokens * MULTIPLIER;
        uint256 denominator = totalTokens * MULTIPLIER;
        uint256 remainder = numerator % denominator;

        // save remainder tokens in the form of decimal numbers with 18 places represented
        // as a uint256
        pollInfo[_pollID].remainder += remainder;

        return numerator / denominator;
    }

    function claimExtraReward(uint _pollID) {
        uint256 reward = pollInfo[_pollID].remainder / MULTIPLIER;
        pollInfo[_pollID].remainder = pollInfo[_pollID].remainder - reward * MULTIPLIER;
        token.transfer(pollInfo[_pollID].claimer, reward);
    }

    // called to move an applying domain to the whitelist
    // iff the domain's challenge period has passed without a challenge
    function moveToRegistry(string _domain) public {
        bytes32 domainHash = sha3(_domain);
        require(appPool[domainHash].challengeTime < now);
        require(appPool[domainHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[domainHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        add(domainHash, appPool[domainHash].owner);
        delete appPool[domainHash].owner;
    }

    // IS RENEWAL OR NOT
    // CHECK IF PAST EXP TIME, IF SO PROCCESS IMMEDIATELY

    // private function to add a domain name to the whitelist
    function add(bytes32 _domainHash, address _owner) private {
        uint expiration = paramSnapshots[_domainHash].registryLen;
        whitelist[_domainHash].expTime = now + expiration;
        whitelist[_domainHash].owner = _owner;
        whitelist[_domainHash].deposit = paramSnapshots[_domainHash].minDeposit;
    }

    // checks if a domain name is in the whitelist and unexpired
    function isVerified(string _domain) public constant returns (bool) {
        bytes32 domainHash = sha3(_domain);
        return whitelist[domainHash].expTime > now;
    }

    // private function to initialize a snapshot of parameters for each application
    function initializeSnapshot(bytes32 _hash) private {
        initializeSnapshotParam(_hash);  // maybe put the two together
        paramSnapshots[_hash].registryLen = get("registryLen");
    }

/*****************************************************************************/

    //called by a user who wishes to change a parameter
    //initializes a proposal to change a parameter
    function proposeUpdate(string _parameter, uint _value) public {
        require(_parameter != "");
        bytes32 parameterHash = sha3(_parameter, _value);
        // initialize application with a with the current values of all parameters
        initializeSnapshotParam(parameterHash);
        initApplication(parameterHash, msg.sender);
        appPool[parameterHash].parameter = _parameter;
        appPool[parameterHash].value = _value;
    }

    //called by user who wishes to reject a proposal
    //initializes a vote to accept/reject the param change proposal
    function challengeProposal(string _parameter, uint _value) public {
        bytes32 parameterHash = sha3(_parameter, _value);
        challenge(parameterHash, msg.sender);
        // start a vote
        uint pollID = callVote(_parameter
        ,paramSnapshots[parameterHash].majority
        ,paramSnapshots[parameterHash].commitVoteLen
        ,paramSnapshots[parameterHash].revealVoteLen);
        idToHash[pollID] = parameterHash;
    }

    // called to change parameter
    // iff the proposal's challenge period has passed without a challenge
    function setParams(string _parameter, uint _value) public {
        bytes32 parameterHash = sha3(_parameter, _value);
        require(appPool[parameterHash].challengeTime < now);
        require(appPool[parameterHash].challenged == false);
        // prevents moving a domain to the registry without ever applying
        require(appPool[parameterHash].owner != 0);
        // prevent applicant from moving to registry multiple times
        Parameters[sha3(_parameter)] = _value;
        delete appPool[parameterHash].owner;
    }

    // a one-time function for each completed vote
    // if proposal won: new parameter value is set, and applicant is rewarded tokens, return true
    // if prospsal lost: challenger is rewarded tokens, return false
    function processProposal(uint _pollID) returns(bool)
    {
        require(pollInfo[_pollID].processed == false);
        bytes32 parameterHash = idToHash[_pollID];
        parameter = appPool[parameterHash].parameter;
        value = appPool[parameterHash].value;
        delete appPool[parameterHash].owner;

        if (voting.isPassed(_pollID)) {
            pollInfo[_pollID].claimer = appPool[parameterHash].owner;
            // setting the value of parameter
            Parameters[sha3(parameter)] = value;
            // give tokens to applicant based on dist and total tokens IMPLEMENT
            giveWinnerReward(parameterHash, appPool[parameterHash].owner);
            token.transfer(appPool[parameterHash].owner, paramSnapshots[parameterHash].minDeposit);
            return true;
        }
        else {
            pollInfo[_pollID].claimer = appPool[parameterHash].challenger;
            // give tokens to challenger based on dist and total tokens
            giveWinnerReward(parameterHash, appPool[parameterHash].challenger);
            token.transfer(appPool[parameterHash].challenger, paramSnapshots[parameterHash].minDeposit);
            return false;
        }
        // ensures the result cannot be processed again
        pollInfo[_pollID].processed = true;
    }

     // private function to initialize a snapshot of parameters for each proposal
     function initializeSnapshotParam(bytes32 _hash) private {
        paramSnapshots[_hash].minDeposit = get("minDeposit");
        paramSnapshots[_hash].challengeLen = get("challengeLen");
        paramSnapshots[_hash].commitVoteLen = get("commitVoteLen");
        paramSnapshots[_hash].revealVoteLen = get("revealVoteLen");
        paramSnapshots[_hash].majority = get("majority");
        paramSnapshots[_hash].dispensationPct = get("dispensationPct");
    }

    // interface for retrieving config parameter from hashmapping
    /// @param _keyword key for hashmap (only useful when keyword matches variable name)
    function get(string _keyword) public constant returns (uint) {
       return Parameters[sha3(_keyword)];
    }

/*****************************************************************************/

    // FOR TESTING
    function toHash(string _domain) returns (bytes32){
        return sha3(_domain);
    }
    function getCurrentTime() returns (uint){
        return now;
    }

}