// Sources flattened with buidler v1.1.2 https://buidler.dev
// File contracts/oz/SafeMath.sol
pragma solidity ^0.5.2;
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0);
uint256 c = a / b;
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
}
// File contracts/oz/IERC20.sol
pragma solidity ^0.5.2;
interface IERC20 {
function transfer(address to, uint256 value) external returns (bool);
function approve(address spender, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
function totalSupply() external view returns (uint256);
function balanceOf(address who) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File contracts/oz/ReentrancyGuard.sol
pragma solidity ^0.5.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* _Since v2.5.0:_ this module is now much more gas efficient, given net gas
* metering changes introduced in the Istanbul hardfork.
*/
contract ReentrancyGuard {
bool private _notEntered;
constructor () internal {
// Storing an initial non-zero value makes deployment a bit more
// expensive, but in exchange the refund on every call to nonReentrant
// will be lower in amount. Since refunds are capped to a percetange of
// the total transaction's gas, it is best to keep them low in cases
// like this one, to increase the likelihood of the full refund coming
// into effect.
_notEntered = true;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_notEntered, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_notEntered = false;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_notEntered = true;
}
}
// File contracts/Moloch.sol
pragma solidity 0.5.12;
contract Moloch is ReentrancyGuard {
using SafeMath for uint256;
/***************
GLOBAL CONSTANTS
***************/
uint256 public periodDuration; // default = 17280 = 4.8 hours in seconds (5 periods per day)
uint256 public votingPeriodLength; // default = 35 periods (7 days)
uint256 public gracePeriodLength; // default = 35 periods (7 days)
uint256 public proposalDeposit; // default = 10 ETH (~$1,000 worth of ETH at contract deployment)
uint256 public dilutionBound; // default = 3 - maximum multiplier a YES voter will be obligated to pay in case of mass ragequit
uint256 public processingReward; // default = 0.1 - amount of ETH to give to whoever processes a proposal
uint256 public summoningTime; // needed to determine the current period
address public depositToken; // deposit token contract reference; default = wETH
// HARD-CODED LIMITS
// These numbers are quite arbitrary; they are small enough to avoid overflows when doing calculations
// with periods or shares, yet big enough to not limit reasonable use cases.
uint256 constant MAX_VOTING_PERIOD_LENGTH = 10**18; // maximum length of voting period
uint256 constant MAX_GRACE_PERIOD_LENGTH = 10**18; // maximum length of grace period
uint256 constant MAX_DILUTION_BOUND = 10**18; // maximum dilution bound
uint256 constant MAX_NUMBER_OF_SHARES_AND_LOOT = 10**18; // maximum number of shares that can be minted
uint256 constant MAX_TOKEN_WHITELIST_COUNT = 400; // maximum number of whitelisted tokens
uint256 constant MAX_TOKEN_GUILDBANK_COUNT = 200; // maximum number of tokens with non-zero balance in guildbank
// ***************
// EVENTS
// ***************
event SummonComplete(address indexed summoner, address[] tokens, uint256 summoningTime, uint256 periodDuration, uint256 votingPeriodLength, uint256 gracePeriodLength, uint256 proposalDeposit, uint256 dilutionBound, uint256 processingReward);
event SubmitProposal(address indexed applicant, uint256 sharesRequested, uint256 lootRequested, uint256 tributeOffered, address tributeToken, uint256 paymentRequested, address paymentToken, string details, bool[6] flags, uint256 proposalId, address indexed delegateKey, address indexed memberAddress);
event SponsorProposal(address indexed delegateKey, address indexed memberAddress, uint256 proposalId, uint256 proposalIndex, uint256 startingPeriod);
event SubmitVote(uint256 proposalId, uint256 indexed proposalIndex, address indexed delegateKey, address indexed memberAddress, uint8 uintVote);
event ProcessProposal(uint256 indexed proposalIndex, uint256 indexed proposalId, bool didPass);
event ProcessWhitelistProposal(uint256 indexed proposalIndex, uint256 indexed proposalId, bool didPass);
event ProcessGuildKickProposal(uint256 indexed proposalIndex, uint256 indexed proposalId, bool didPass);
event Ragequit(address indexed memberAddress, uint256 sharesToBurn, uint256 lootToBurn);
event TokensCollected(address indexed token, uint256 amountToCollect);
event CancelProposal(uint256 indexed proposalId, address applicantAddress);
event UpdateDelegateKey(address indexed memberAddress, address newDelegateKey);
event Withdraw(address indexed memberAddress, address token, uint256 amount);
// *******************
// INTERNAL ACCOUNTING
// *******************
uint256 public proposalCount = 0; // total proposals submitted
uint256 public totalShares = 0; // total shares across all members
uint256 public totalLoot = 0; // total loot across all members
uint256 public totalGuildBankTokens = 0; // total tokens with non-zero balance in guild bank
address public constant GUILD = address(0xdead);
address public constant ESCROW = address(0xbeef);
address public constant TOTAL = address(0xbabe);
mapping (address => mapping(address => uint256)) public userTokenBalances; // userTokenBalances[userAddress][tokenAddress]
enum Vote {
Null, // default value, counted as abstention
Yes,
No
}
struct Member {
address delegateKey; // the key responsible for submitting proposals and voting - defaults to member address unless updated
uint256 shares; // the # of voting shares assigned to this member
uint256 loot; // the loot amount available to this member (combined with shares on ragequit)
bool exists; // always true once a member has been created
uint256 highestIndexYesVote; // highest proposal index # on which the member voted YES
uint256 jailed; // set to proposalIndex of a passing guild kick proposal for this member, prevents voting on and sponsoring proposals
}
struct Proposal {
address applicant; // the applicant who wishes to become a member - this key will be used for withdrawals (doubles as guild kick target for gkick proposals)
address proposer; // the account that submitted the proposal (can be non-member)
address sponsor; // the member that sponsored the proposal (moving it into the queue)
uint256 sharesRequested; // the # of shares the applicant is requesting
uint256 lootRequested; // the amount of loot the applicant is requesting
uint256 tributeOffered; // amount of tokens offered as tribute
address tributeToken; // tribute token contract reference
uint256 paymentRequested; // amount of tokens requested as payment
address paymentToken; // payment token contract reference
uint256 startingPeriod; // the period in which voting can start for this proposal
uint256 yesVotes; // the total number of YES votes for this proposal
uint256 noVotes; // the total number of NO votes for this proposal
bool[6] flags; // [sponsored, processed, didPass, cancelled, whitelist, guildkick]
string details; // proposal details - could be IPFS hash, plaintext, or JSON
uint256 maxTotalSharesAndLootAtYesVote; // the maximum # of total shares encountered at a yes vote on this proposal
mapping(address => Vote) votesByMember; // the votes on this proposal by each member
}
mapping(address => bool) public tokenWhitelist;
address[] public approvedTokens;
mapping(address => bool) public proposedToWhitelist;
mapping(address => bool) public proposedToKick;
mapping(address => Member) public members;
mapping(address => address) public memberAddressByDelegateKey;
mapping(uint256 => Proposal) public proposals;
uint256[] public proposalQueue;
modifier onlyMember {
require(members[msg.sender].shares > 0 || members[msg.sender].loot > 0, "not a member");
_;
}
modifier onlyShareholder {
require(members[msg.sender].shares > 0, "not a shareholder");
_;
}
modifier onlyDelegate {
require(members[memberAddressByDelegateKey[msg.sender]].shares > 0, "not a delegate");
_;
}
constructor(
address _summoner,
address[] memory _approvedTokens,
uint256 _periodDuration,
uint256 _votingPeriodLength,
uint256 _gracePeriodLength,
uint256 _proposalDeposit,
uint256 _dilutionBound,
uint256 _processingReward
) public {
require(_summoner != address(0), "summoner cannot be 0");
require(_periodDuration > 0, "_periodDuration cannot be 0");
require(_votingPeriodLength > 0, "_votingPeriodLength cannot be 0");
require(_votingPeriodLength <= MAX_VOTING_PERIOD_LENGTH, "_votingPeriodLength exceeds limit");
require(_gracePeriodLength <= MAX_GRACE_PERIOD_LENGTH, "_gracePeriodLength exceeds limit");
require(_dilutionBound > 0, "_dilutionBound cannot be 0");
require(_dilutionBound <= MAX_DILUTION_BOUND, "_dilutionBound exceeds limit");
require(_approvedTokens.length > 0, "need at least one approved token");
require(_approvedTokens.length <= MAX_TOKEN_WHITELIST_COUNT, "too many tokens");
require(_proposalDeposit >= _processingReward, "_proposalDeposit cannot be smaller than _processingReward");
depositToken = _approvedTokens[0];
// NOTE: move event up here, avoid stack too deep if too many approved tokens
emit SummonComplete(_summoner, _approvedTokens, now, _periodDuration, _votingPeriodLength, _gracePeriodLength, _proposalDeposit, _dilutionBound, _processingReward);
for (uint256 i = 0; i < _approvedTokens.length; i++) {
require(_approvedTokens[i] != address(0), "_approvedToken cannot be 0");
require(!tokenWhitelist[_approvedTokens[i]], "duplicate approved token");
tokenWhitelist[_approvedTokens[i]] = true;
approvedTokens.push(_approvedTokens[i]);
}
periodDuration = _periodDuration;
votingPeriodLength = _votingPeriodLength;
gracePeriodLength = _gracePeriodLength;
proposalDeposit = _proposalDeposit;
dilutionBound = _dilutionBound;
processingReward = _processingReward;
summoningTime = now;
members[_summoner] = Member(_summoner, 1, 0, true, 0, 0);
memberAddressByDelegateKey[_summoner] = _summoner;
totalShares = 1;
}
/*****************
PROPOSAL FUNCTIONS
*****************/
function submitProposal(
address applicant,
uint256 sharesRequested,
uint256 lootRequested,
uint256 tributeOffered,
address tributeToken,
uint256 paymentRequested,
address paymentToken,
string memory details
) public nonReentrant returns (uint256 proposalId) {
require(sharesRequested.add(lootRequested) <= MAX_NUMBER_OF_SHARES_AND_LOOT, "too many shares requested");
require(tokenWhitelist[tributeToken], "tributeToken is not whitelisted");
require(tokenWhitelist[paymentToken], "payment is not whitelisted");
require(applicant != address(0), "applicant cannot be 0");
require(applicant != GUILD && applicant != ESCROW && applicant != TOTAL, "applicant address cannot be reserved");
require(members[applicant].jailed == 0, "proposal applicant must not be jailed");
if (tributeOffered > 0 && userTokenBalances[GUILD][tributeToken] == 0) {
require(totalGuildBankTokens < MAX_TOKEN_GUILDBANK_COUNT, 'cannot submit more tribute proposals for new tokens - guildbank is full');
}
// collect tribute from proposer and store it in the Moloch until the proposal is processed
require(IERC20(tributeToken).transferFrom(msg.sender, address(this), tributeOffered), "tribute token transfer failed");
unsafeAddToBalance(ESCROW, tributeToken, tributeOffered);
bool[6] memory flags; // [sponsored, processed, didPass, cancelled, whitelist, guildkick]
_submitProposal(applicant, sharesRequested, lootRequested, tributeOffered, tributeToken, paymentRequested, paymentToken, details, flags);
return proposalCount - 1; // return proposalId - contracts calling submit might want it
}
function submitWhitelistProposal(address tokenToWhitelist, string memory details) public nonReentrant returns (uint256 proposalId) {
require(tokenToWhitelist != address(0), "must provide token address");
require(!tokenWhitelist[tokenToWhitelist], "cannot already have whitelisted the token");
require(approvedTokens.length < MAX_TOKEN_WHITELIST_COUNT, "cannot submit more whitelist proposals");
bool[6] memory flags; // [sponsored, processed, didPass, cancelled, whitelist, guildkick]
flags[4] = true; // whitelist
_submitProposal(address(0), 0, 0, 0, tokenToWhitelist, 0, address(0), details, flags);
return proposalCount - 1;
}
function submitGuildKickProposal(address memberToKick, string memory details) public nonReentrant returns (uint256 proposalId) {
Member memory member = members[memberToKick];
require(member.shares > 0 || member.loot > 0, "member must have at least one share or one loot");
require(members[memberToKick].jailed == 0, "member must not already be jailed");
bool[6] memory flags; // [sponsored, processed, didPass, cancelled, whitelist, guildkick]
flags[5] = true; // guild kick
_submitProposal(memberToKick, 0, 0, 0, address(0), 0, address(0), details, flags);
return proposalCount - 1;
}
function _submitProposal(
address applicant,
uint256 sharesRequested,
uint256 lootRequested,
uint256 tributeOffered,
address tributeToken,
uint256 paymentRequested,
address paymentToken,
string memory details,
bool[6] memory flags
) internal {
Proposal memory proposal = Proposal({
applicant : applicant,
proposer : msg.sender,
sponsor : address(0),
sharesRequested : sharesRequested,
lootRequested : lootRequested,
tributeOffered : tributeOffered,
tributeToken : tributeToken,
paymentRequested : paymentRequested,
paymentToken : paymentToken,
startingPeriod : 0,
yesVotes : 0,
noVotes : 0,
flags : flags,
details : details,
maxTotalSharesAndLootAtYesVote : 0
});
proposals[proposalCount] = proposal;
address memberAddress = memberAddressByDelegateKey[msg.sender];
// NOTE: argument order matters, avoid stack too deep
emit SubmitProposal(applicant, sharesRequested, lootRequested, tributeOffered, tributeToken, paymentRequested, paymentToken, details, flags, proposalCount, msg.sender, memberAddress);
proposalCount += 1;
}
function sponsorProposal(uint256 proposalId) public nonReentrant onlyDelegate {
// collect proposal deposit from sponsor and store it in the Moloch until the proposal is processed
require(IERC20(depositToken).transferFrom(msg.sender, address(this), proposalDeposit), "proposal deposit token transfer failed");
unsafeAddToBalance(ESCROW, depositToken, proposalDeposit);
Proposal storage proposal = proposals[proposalId];
require(proposal.proposer != address(0), 'proposal must have been proposed');
require(!proposal.flags[0], "proposal has already been sponsored");
require(!proposal.flags[3], "proposal has been cancelled");
require(members[proposal.applicant].jailed == 0, "proposal applicant must not be jailed");
if (proposal.tributeOffered > 0 && userTokenBalances[GUILD][proposal.tributeToken] == 0) {
require(totalGuildBankTokens < MAX_TOKEN_GUILDBANK_COUNT, 'cannot sponsor more tribute proposals for new tokens - guildbank is full');
}
// whitelist proposal
if (proposal.flags[4]) {
require(!tokenWhitelist[address(proposal.tributeToken)], "cannot already have whitelisted the token");
require(!proposedToWhitelist[address(proposal.tributeToken)], 'already proposed to whitelist');
require(approvedTokens.length < MAX_TOKEN_WHITELIST_COUNT, "cannot sponsor more whitelist proposals");
proposedToWhitelist[address(proposal.tributeToken)] = true;
// guild kick proposal
} else if (proposal.flags[5]) {
require(!proposedToKick[proposal.applicant], 'already proposed to kick');
proposedToKick[proposal.applicant] = true;
}
// compute startingPeriod for proposal
uint256 startingPeriod = max(
getCurrentPeriod(),
proposalQueue.length == 0 ? 0 : proposals[proposalQueue[proposalQueue.length.sub(1)]].startingPeriod
).add(1);
proposal.startingPeriod = startingPeriod;
address memberAddress = memberAddressByDelegateKey[msg.sender];
proposal.sponsor = memberAddress;
proposal.flags[0] = true; // sponsored
// append proposal to the queue
proposalQueue.push(proposalId);
emit SponsorProposal(msg.sender, memberAddress, proposalId, proposalQueue.length.sub(1), startingPeriod);
}
// NOTE: In MolochV2 proposalIndex !== proposalId
function submitVote(uint256 proposalIndex, uint8 uintVote) public nonReentrant onlyDelegate {
address memberAddress = memberAddressByDelegateKey[msg.sender];
Member storage member = members[memberAddress];
require(proposalIndex < proposalQueue.length, "proposal does not exist");
Proposal storage proposal = proposals[proposalQueue[proposalIndex]];
require(uintVote < 3, "must be less than 3");
Vote vote = Vote(uintVote);
require(getCurrentPeriod() >= proposal.startingPeriod, "voting period has not started");
require(!hasVotingPeriodExpired(proposal.startingPeriod), "proposal voting period has expired");
require(proposal.votesByMember[memberAddress] == Vote.Null, "member has already voted");
require(vote == Vote.Yes || vote == Vote.No, "vote must be either Yes or No");
proposal.votesByMember[memberAddress] = vote;
if (vote == Vote.Yes) {
proposal.yesVotes = proposal.yesVotes.add(member.shares);
// set highest index (latest) yes vote - must be processed for member to ragequit
if (proposalIndex > member.highestIndexYesVote) {
member.highestIndexYesVote = proposalIndex;
}
// set maximum of total shares encountered at a yes vote - used to bound dilution for yes voters
if (totalShares.add(totalLoot) > proposal.maxTotalSharesAndLootAtYesVote) {
proposal.maxTotalSharesAndLootAtYesVote = totalShares.add(totalLoot);
}
} else if (vote == Vote.No) {
proposal.noVotes = proposal.noVotes.add(member.shares);
}
// NOTE: subgraph indexes by proposalId not proposalIndex since proposalIndex isn't set untill it's been sponsored but proposal is created on submission
emit SubmitVote(proposalQueue[proposalIndex], proposalIndex, msg.sender, memberAddress, uintVote);
}
function processProposal(uint256 proposalIndex) public nonReentrant {
_validateProposalForProcessing(proposalIndex);
uint256 proposalId = proposalQueue[proposalIndex];
Proposal storage proposal = proposals[proposalId];
require(!proposal.flags[4] && !proposal.flags[5], "must be a standard proposal");
proposal.flags[1] = true; // processed
bool didPass = _didPass(proposalIndex);
// Make the proposal fail if the new total number of shares and loot exceeds the limit
if (totalShares.add(totalLoot).add(proposal.sharesRequested).add(proposal.lootRequested) > MAX_NUMBER_OF_SHARES_AND_LOOT) {
didPass = false;
}
// Make the proposal fail if it is requesting more tokens as payment than the available guild bank balance
if (proposal.paymentRequested > userTokenBalances[GUILD][proposal.paymentToken]) {
didPass = false;
}
// Make the proposal fail if it would result in too many tokens with non-zero balance in guild bank
if (proposal.tributeOffered > 0 && userTokenBalances[GUILD][proposal.tributeToken] == 0 && totalGuildBankTokens >= MAX_TOKEN_GUILDBANK_COUNT) {
didPass = false;
}
// PROPOSAL PASSED
if (didPass) {
proposal.flags[2] = true; // didPass
// if the applicant is already a member, add to their existing shares & loot
if (members[proposal.applicant].exists) {
members[proposal.applicant].shares = members[proposal.applicant].shares.add(proposal.sharesRequested);
members[proposal.applicant].loot = members[proposal.applicant].loot.add(proposal.lootRequested);
// the applicant is a new member, create a new record for them
} else {
// if the applicant address is already taken by a member's delegateKey, reset it to their member address
if (members[memberAddressByDelegateKey[proposal.applicant]].exists) {
address memberToOverride = memberAddressByDelegateKey[proposal.applicant];
memberAddressByDelegateKey[memberToOverride] = memberToOverride;
members[memberToOverride].delegateKey = memberToOverride;
}
// use applicant address as delegateKey by default
members[proposal.applicant] = Member(proposal.applicant, proposal.sharesRequested, proposal.lootRequested, true, 0, 0);
memberAddressByDelegateKey[proposal.applicant] = proposal.applicant;
}
// mint new shares & loot
totalShares = totalShares.add(proposal.sharesRequested);
totalLoot = totalLoot.add(proposal.lootRequested);
// if the proposal tribute is the first tokens of its kind to make it into the guild bank, increment total guild bank tokens
if (userTokenBalances[GUILD][proposal.tributeToken] == 0 && proposal.tributeOffered > 0) {
totalGuildBankTokens += 1;
}
unsafeInternalTransfer(ESCROW, GUILD, proposal.tributeToken, proposal.tributeOffered);
unsafeInternalTransfer(GUILD, proposal.applicant, proposal.paymentToken, proposal.paymentRequested);
// if the proposal spends 100% of guild bank balance for a token, decrement total guild bank tokens
if (userTokenBalances[GUILD][proposal.paymentToken] == 0 && proposal.paymentRequested > 0) {
totalGuildBankTokens -= 1;
}
// PROPOSAL FAILED
} else {
// return all tokens to the proposer (not the applicant, because funds come from proposer)
unsafeInternalTransfer(ESCROW, proposal.proposer, proposal.tributeToken, proposal.tributeOffered);
}
_returnDeposit(proposal.sponsor);
emit ProcessProposal(proposalIndex, proposalId, didPass);
}
function processWhitelistProposal(uint256 proposalIndex) public nonReentrant {
_validateProposalForProcessing(proposalIndex);
uint256 proposalId = proposalQueue[proposalIndex];
Proposal storage proposal = proposals[proposalId];
require(proposal.flags[4], "must be a whitelist proposal");
proposal.flags[1] = true; // processed
bool didPass = _didPass(proposalIndex);
if (approvedTokens.length >= MAX_TOKEN_WHITELIST_COUNT) {
didPass = false;
}
if (didPass) {
proposal.flags[2] = true; // didPass
tokenWhitelist[address(proposal.tributeToken)] = true;
approvedTokens.push(proposal.tributeToken);
}
proposedToWhitelist[address(proposal.tributeToken)] = false;
_returnDeposit(proposal.sponsor);
emit ProcessWhitelistProposal(proposalIndex, proposalId, didPass);
}
function processGuildKickProposal(uint256 proposalIndex) public nonReentrant {
_validateProposalForProcessing(proposalIndex);
uint256 proposalId = proposalQueue[proposalIndex];
Proposal storage proposal = proposals[proposalId];
require(proposal.flags[5], "must be a guild kick proposal");
proposal.flags[1] = true; // processed
bool didPass = _didPass(proposalIndex);
if (didPass) {
proposal.flags[2] = true; // didPass
Member storage member = members[proposal.applicant];
member.jailed = proposalIndex;
// transfer shares to loot
member.loot = member.loot.add(member.shares);
totalShares = totalShares.sub(member.shares);
totalLoot = totalLoot.add(member.shares);
member.shares = 0; // revoke all shares
}
proposedToKick[proposal.applicant] = false;
_returnDeposit(proposal.sponsor);
emit ProcessGuildKickProposal(proposalIndex, proposalId, didPass);
}
function _didPass(uint256 proposalIndex) internal returns (bool didPass) {
Proposal memory proposal = proposals[proposalQueue[proposalIndex]];
didPass = proposal.yesVotes > proposal.noVotes;
// Make the proposal fail if the dilutionBound is exceeded
if ((totalShares.add(totalLoot)).mul(dilutionBound) < proposal.maxTotalSharesAndLootAtYesVote) {
didPass = false;
}
// Make the proposal fail if the applicant is jailed
// - for standard proposals, we don't want the applicant to get any shares/loot/payment
// - for guild kick proposals, we should never be able to propose to kick a jailed member (or have two kick proposals active), so it doesn't matter
if (members[proposal.applicant].jailed != 0) {
didPass = false;
}
return didPass;
}
function _validateProposalForProcessing(uint256 proposalIndex) internal view {
require(proposalIndex < proposalQueue.length, "proposal does not exist");
Proposal memory proposal = proposals[proposalQueue[proposalIndex]];
require(getCurrentPeriod() >= proposal.startingPeriod.add(votingPeriodLength).add(gracePeriodLength), "proposal is not ready to be processed");
require(proposal.flags[1] == false, "proposal has already been processed");
require(proposalIndex == 0 || proposals[proposalQueue[proposalIndex.sub(1)]].flags[1], "previous proposal must be processed");
}
function _returnDeposit(address sponsor) internal {
unsafeInternalTransfer(ESCROW, msg.sender, depositToken, processingReward);
unsafeInternalTransfer(ESCROW, sponsor, depositToken, proposalDeposit.sub(processingReward));
}
function ragequit(uint256 sharesToBurn, uint256 lootToBurn) public nonReentrant onlyMember {
_ragequit(msg.sender, sharesToBurn, lootToBurn);
}
function _ragequit(address memberAddress, uint256 sharesToBurn, uint256 lootToBurn) internal {
uint256 initialTotalSharesAndLoot = totalShares.add(totalLoot);
Member storage member = members[memberAddress];
require(member.shares >= sharesToBurn, "insufficient shares");
require(member.loot >= lootToBurn, "insufficient loot");
require(canRagequit(member.highestIndexYesVote), "cannot ragequit until highest index proposal member voted YES on is processed");
uint256 sharesAndLootToBurn = sharesToBurn.add(lootToBurn);
// burn shares and loot
member.shares = member.shares.sub(sharesToBurn);
member.loot = member.loot.sub(lootToBurn);
totalShares = totalShares.sub(sharesToBurn);
totalLoot = totalLoot.sub(lootToBurn);
for (uint256 i = 0; i < approvedTokens.length; i++) {
uint256 amountToRagequit = fairShare(userTokenBalances[GUILD][approvedTokens[i]], sharesAndLootToBurn, initialTotalSharesAndLoot);
if (amountToRagequit > 0) { // gas optimization to allow a higher maximum token limit
// deliberately not using safemath here to keep overflows from preventing the function execution (which would break ragekicks)
// if a token overflows, it is because the supply was artificially inflated to oblivion, so we probably don't care about it anyways
userTokenBalances[GUILD][approvedTokens[i]] -= amountToRagequit;
userTokenBalances[memberAddress][approvedTokens[i]] += amountToRagequit;
}
}
emit Ragequit(msg.sender, sharesToBurn, lootToBurn);
}
function ragekick(address memberToKick) public nonReentrant {
Member storage member = members[memberToKick];
require(member.jailed != 0, "member must be in jail");
require(member.loot > 0, "member must have some loot"); // note - should be impossible for jailed member to have shares
require(canRagequit(member.highestIndexYesVote), "cannot ragequit until highest index proposal member voted YES on is processed");
_ragequit(memberToKick, 0, member.loot);
}
function withdrawBalance(address token, uint256 amount) public nonReentrant {
_withdrawBalance(token, amount);
}
function withdrawBalances(address[] memory tokens, uint256[] memory amounts, bool max) public nonReentrant {
require(tokens.length == amounts.length, "tokens and amounts arrays must be matching lengths");
for (uint256 i=0; i < tokens.length; i++) {
uint256 withdrawAmount = amounts[i];
if (max) { // withdraw the maximum balance
withdrawAmount = userTokenBalances[msg.sender][tokens[i]];
}
_withdrawBalance(tokens[i], withdrawAmount);
}
}
function _withdrawBalance(address token, uint256 amount) internal {
require(userTokenBalances[msg.sender][token] >= amount, "insufficient balance");
unsafeSubtractFromBalance(msg.sender, token, amount);
require(IERC20(token).transfer(msg.sender, amount), "transfer failed");
emit Withdraw(msg.sender, token, amount);
}
function collectTokens(address token) public onlyDelegate nonReentrant {
uint256 amountToCollect = IERC20(token).balanceOf(address(this)).sub(userTokenBalances[TOTAL][token]);
// only collect if 1) there are tokens to collect 2) token is whitelisted 3) token has non-zero balance
require(amountToCollect > 0, 'no tokens to collect');
require(tokenWhitelist[token], 'token to collect must be whitelisted');
require(userTokenBalances[GUILD][token] > 0, 'token to collect must have non-zero guild bank balance');
unsafeAddToBalance(GUILD, token, amountToCollect);
emit TokensCollected(token, amountToCollect);
}
// NOTE: requires that delegate key which sent the original proposal cancels, msg.sender == proposal.proposer
function cancelProposal(uint256 proposalId) public nonReentrant {
Proposal storage proposal = proposals[proposalId];
require(!proposal.flags[0], "proposal has already been sponsored");
require(!proposal.flags[3], "proposal has already been cancelled");
require(msg.sender == proposal.proposer, "solely the proposer can cancel");
proposal.flags[3] = true; // cancelled
unsafeInternalTransfer(ESCROW, proposal.proposer, proposal.tributeToken, proposal.tributeOffered);
emit CancelProposal(proposalId, msg.sender);
}
function updateDelegateKey(address newDelegateKey) public nonReentrant onlyShareholder {
require(newDelegateKey != address(0), "newDelegateKey cannot be 0");
// skip checks if member is setting the delegate key to their member address
if (newDelegateKey != msg.sender) {
require(!members[newDelegateKey].exists, "cannot overwrite existing members");
require(!members[memberAddressByDelegateKey[newDelegateKey]].exists, "cannot overwrite existing delegate keys");
}
Member storage member = members[msg.sender];
memberAddressByDelegateKey[member.delegateKey] = address(0);
memberAddressByDelegateKey[newDelegateKey] = msg.sender;
member.delegateKey = newDelegateKey;
emit UpdateDelegateKey(msg.sender, newDelegateKey);
}
// can only ragequit if the latest proposal you voted YES on has been processed
function canRagequit(uint256 highestIndexYesVote) public view returns (bool) {
require(highestIndexYesVote < proposalQueue.length, "proposal does not exist");
return proposals[proposalQueue[highestIndexYesVote]].flags[1];
}
function hasVotingPeriodExpired(uint256 startingPeriod) public view returns (bool) {
return getCurrentPeriod() >= startingPeriod.add(votingPeriodLength);
}
/***************
GETTER FUNCTIONS
***************/
function max(uint256 x, uint256 y) internal pure returns (uint256) {
return x >= y ? x : y;
}
function getCurrentPeriod() public view returns (uint256) {
return now.sub(summoningTime).div(periodDuration);
}
function getProposalQueueLength() public view returns (uint256) {
return proposalQueue.length;
}
function getProposalFlags(uint256 proposalId) public view returns (bool[6] memory) {
return proposals[proposalId].flags;
}
function getUserTokenBalance(address user, address token) public view returns (uint256) {
return userTokenBalances[user][token];
}
function getMemberProposalVote(address memberAddress, uint256 proposalIndex) public view returns (Vote) {
require(members[memberAddress].exists, "member does not exist");
require(proposalIndex < proposalQueue.length, "proposal does not exist");
return proposals[proposalQueue[proposalIndex]].votesByMember[memberAddress];
}
function getTokenCount() public view returns (uint256) {
return approvedTokens.length;
}
/***************
HELPER FUNCTIONS
***************/
function unsafeAddToBalance(address user, address token, uint256 amount) internal {
userTokenBalances[user][token] += amount;
userTokenBalances[TOTAL][token] += amount;
}
function unsafeSubtractFromBalance(address user, address token, uint256 amount) internal {
userTokenBalances[user][token] -= amount;
userTokenBalances[TOTAL][token] -= amount;
}
function unsafeInternalTransfer(address from, address to, address token, uint256 amount) internal {
unsafeSubtractFromBalance(from, token, amount);
unsafeAddToBalance(to, token, amount);
}
function fairShare(uint256 balance, uint256 shares, uint256 totalShares) internal pure returns (uint256) {
require(totalShares != 0);
if (balance == 0) { return 0; }
uint256 prod = balance * shares;
if (prod / balance == shares) { // no overflow in multiplication above?
return prod / totalShares;
}
return (balance / totalShares) * shares;
}
}
// File contracts/MolochSummoner.sol
pragma solidity 0.5.12;
contract MolochSummoner {
Moloch private M;
address[] public Molochs;
event Summoned(address indexed M, address indexed _summoner);
function summonMoloch(
address _summoner,
address[] memory _approvedTokens,
uint256 _periodDuration,
uint256 _votingPeriodLength,
uint256 _gracePeriodLength,
uint256 _proposalDeposit,
uint256 _dilutionBound,
uint256 _processingReward) public {
M = new Moloch(
_summoner,
_approvedTokens,
_periodDuration,
_votingPeriodLength,
_gracePeriodLength,
_proposalDeposit,
_dilutionBound,
_processingReward);
Molochs.push(address(M));
emit Summoned(address(M), _summoner);
}
function getMolochCount() public view returns (uint256 MolochCount) {
return Molochs.length;
}
}
// File contracts/oz/ERC20.sol
pragma solidity ^0.5.2;
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
* Originally based on code by FirstBlood:
* https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol
*
* This implementation emits additional Approval events, allowing applications to reconstruct the allowance status for
* all accounts just by listening to said events. Note that this isn't required by the specification, and other
* compliant implementations may not do it.
*/
contract ERC20 is IERC20 {
using SafeMath for uint256;
mapping (address => uint256) private _balances;
mapping (address => mapping (address => uint256)) private _allowed;
uint256 private _totalSupply;
/**
* @dev Total number of tokens in existence
*/
function totalSupply() public view returns (uint256) {
return _totalSupply;
}
/**
* @dev Gets the balance of the specified address.
* @param owner The address to query the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param owner address The address which owns the funds.
* @param spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address owner, address spender) public view returns (uint256) {
return _allowed[owner][spender];
}
/**
* @dev Transfer token for a specified address
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function transfer(address to, uint256 value) public returns (bool) {
_transfer(msg.sender, to, value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
* Beware that changing an allowance with this method brings the risk that someone may use both the old
* and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
* race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
*/
function approve(address spender, uint256 value) public returns (bool) {
_approve(msg.sender, spender, value);
return true;
}
/**
* @dev Transfer tokens from one address to another.
* Note that while this function emits an Approval event, this is not required as per the specification,
* and other compliant implementations may not emit the event.
* @param from address The address which you want to send tokens from
* @param to address The address which you want to transfer to
* @param value uint256 the amount of tokens to be transferred
*/
function transferFrom(address from, address to, uint256 value) public returns (bool) {
_transfer(from, to, value);
_approve(from, msg.sender, _allowed[from][msg.sender].sub(value));
return true;
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
* approve should be called when allowed_[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* Emits an Approval event.
* @param spender The address which will spend the funds.
* @param addedValue The amount of tokens to increase the allowance by.
*/
function increaseAllowance(address spender, uint256 addedValue) public returns (bool) {
_approve(msg.sender, spender, _allowed[msg.sender][spender].add(addedValue));
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
* approve should be called when allowed_[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* Emits an Approval event.
* @param spender The address which will spend the funds.
* @param subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) {
_approve(msg.sender, spender, _allowed[msg.sender][spender].sub(subtractedValue));
return true;
}
/**
* @dev Transfer token for a specified addresses
* @param from The address to transfer from.
* @param to The address to transfer to.
* @param value The amount to be transferred.
*/
function _transfer(address from, address to, uint256 value) internal {
require(to != address(0));
_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(from, to, value);
}
/**
* @dev Internal function that mints an amount of the token and assigns it to
* an account. This encapsulates the modification of balances such that the
* proper events are emitted.
* @param account The account that will receive the created tokens.
* @param value The amount that will be created.
*/
function _mint(address account, uint256 value) internal {
require(account != address(0));
_totalSupply = _totalSupply.add(value);
_balances[account] = _balances[account].add(value);
emit Transfer(address(0), account, value);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account.
* @param account The account whose tokens will be burnt.
* @param value The amount that will be burnt.
*/
function _burn(address account, uint256 value) internal {
require(account != address(0));
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
/**
* @dev Approve an address to spend another addresses' tokens.
* @param owner The address that owns the tokens.
* @param spender The address that will spend the tokens.
* @param value The number of tokens that can be spent.
*/
function _approve(address owner, address spender, uint256 value) internal {
require(spender != address(0));
require(owner != address(0));
_allowed[owner][spender] = value;
emit Approval(owner, spender, value);
}
/**
* @dev Internal function that burns an amount of the token of a given
* account, deducting from the sender's allowance for said account. Uses the
* internal burn function.
* Emits an Approval event (reflecting the reduced allowance).
* @param account The account whose tokens will be burnt.
* @param value The amount that will be burnt.
*/
function _burnFrom(address account, uint256 value) internal {
_burn(account, value);
_approve(account, msg.sender, _allowed[account][msg.sender].sub(value));
}
}
// File contracts/test-helpers/Submitter.sol
// helper for testing moloch.submitProposal return value
pragma solidity 0.5.12;
contract Submitter {
event Submit(uint256 proposalId);
Moloch public moloch; // moloch contract reference
constructor(address molochAddress) public {
moloch = Moloch(molochAddress);
}
function submitProposal(
address applicant,
uint256 sharesRequested,
uint256 lootRequested,
uint256 tributeOffered,
address tributeToken,
uint256 paymentRequested,
address paymentToken,
string memory details
) public {
uint256 proposalId = moloch.submitProposal(
applicant,
sharesRequested,
lootRequested,
tributeOffered,
tributeToken,
paymentRequested,
paymentToken,
details
);
emit Submit(proposalId);
}
function submitWhitelistProposal(
address tokenToWhitelist,
string memory details
) public {
uint256 proposalId = moloch.submitWhitelistProposal(
tokenToWhitelist,
details
);
emit Submit(proposalId);
}
function submitGuildKickProposal(
address memberToKick,
string memory details
) public {
uint256 proposalId = moloch.submitGuildKickProposal(
memberToKick,
details
);
emit Submit(proposalId);
}
}
// File contracts/tokens/ClaimsToken.sol
pragma solidity ^0.5.2;
/**
* @dev Optional functions from the ERC20 standard.
*/
contract ERC20Detailed is IERC20 {
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for `name`, `symbol`, and `decimals`. All three of
* these values are immutable: they can only be set once during
* construction.
*/
constructor (string memory name, string memory symbol, uint8 decimals) public {
_name = name;
_symbol = symbol;
_decimals = decimals;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
}
interface IClaimsToken {
/**
* @dev This event emits when funds to be deposited are sent to the token contract
* @param from contains the address of the sender of the received funds
* @param fundsReceived contains the amount of funds received for distribution
*/
event FundsReceived(address indexed from, uint256 fundsReceived);
/**
* @dev This event emits when distributed funds are withdrawn by a token holder.
* @param by contains the address of the receiver of funds
* @param fundsWithdrawn contains the amount of funds that were withdrawn
*/
event FundsWithdrawn(address indexed by, uint256 fundsWithdrawn);
/**
* @dev Withdraws available funds for user.
*/
function withdrawFunds() external payable;
/**
* @dev Returns the amount of funds a given address is able to withdraw currently.
* @param _forAddress Address of ClaimsToken holder
* @return A uint256 representing the available funds for a given account
*/
function availableFunds(address _forAddress) external view returns (uint256);
/**
* @dev Get cumulative funds received by ClaimsToken.
* @return A uint256 representing the total funds received by ClaimsToken
*/
function totalReceivedFunds() external view returns (uint256);
}
contract ClaimsToken is IClaimsToken, ERC20, ERC20Detailed {
using SafeMath for uint256;
// cumulative funds received by this contract
uint256 public receivedFunds;
// cumulative funds received which were already processed for distribution - by user
mapping (address => uint256) public processedFunds;
// claimed but not yet withdrawn funds for a user
mapping (address => uint256) public claimedFunds;
constructor(address _owner)
public
ERC20Detailed("ClaimsToken", "CST", 18)
{
_mint(_owner, 10000 * (10 ** uint256(18)));
receivedFunds = 0;
}
/**
* @dev Transfer token to a specified address.
* Claims funds for both parties, whereby the amount of tokens withdrawn
* is inherited by the new token owner.
* @param _to The address to transfer to
* @param _value The amount to be transferred
*/
function transfer(address _to, uint256 _value)
public
returns (bool)
{
_claimFunds(msg.sender);
_claimFunds(_to);
return super.transfer(_to, _value);
}
/**
* @dev Transfer tokens from one address to another.
* Claims funds for both parties, whereby the amount of tokens withdrawn
* is inherited by the new token owner.
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value)
public
returns (bool)
{
_claimFunds(_from);
_claimFunds(_to);
return super.transferFrom(_from, _to, _value);
}
/**
* @dev Get cumulative funds received by ClaimsToken.
* @return A uint256 representing the total funds received by ClaimsToken
*/
function totalReceivedFunds()
external
view
returns (uint256)
{
return receivedFunds;
}
/**
* @dev Returns the amount of funds a given address is able to withdraw currently.
* @param _forAddress Address of ClaimsToken holder
* @return A uint256 representing the available funds for a given account
*/
function availableFunds(address _forAddress)
public
view
returns (uint256)
{
return _calcUnprocessedFunds(_forAddress).add(claimedFunds[_forAddress]);
}
/**
* @dev Increments cumulative received funds by new received funds.
* Called when ClaimsToken receives funds.
* @param _value Amount of tokens / Ether received
*/
function _registerFunds(uint256 _value)
internal
{
receivedFunds = receivedFunds.add(_value);
}
/**
* @dev Returns payout for a user which can be withdrawn or claimed.
* @param _forAddress Address of ClaimsToken holder
*/
function _calcUnprocessedFunds(address _forAddress)
internal
view
returns (uint256)
{
uint256 newReceivedFunds = receivedFunds.sub(processedFunds[_forAddress]);
return balanceOf(_forAddress).mul(newReceivedFunds).div(totalSupply());
}
/**
* @dev Claims funds for a user.
* @param _forAddress Address of ClaimsToken holder
*/
function _claimFunds(address _forAddress) internal {
uint256 unprocessedFunds = _calcUnprocessedFunds(_forAddress);
processedFunds[_forAddress] = receivedFunds;
claimedFunds[_forAddress] = claimedFunds[_forAddress].add(unprocessedFunds);
}
/**
* @dev Sets claimed but not yet withdrawn funds to 0,
* marks total received funds as processed and
* returns the withdrawable amount for a user.
* @return A uint256 representing the withdrawable funds
*/
function _prepareWithdraw()
internal
returns (uint256)
{
uint256 withdrawableFunds = availableFunds(msg.sender);
processedFunds[msg.sender] = receivedFunds;
claimedFunds[msg.sender] = 0;
return withdrawableFunds;
}
}
contract ClaimsTokenERC20Extension is IClaimsToken, ClaimsToken {
// token that ClaimsToken takes in custodianship
IERC20 public fundsToken;
modifier onlyFundsToken () {
require(msg.sender == address(fundsToken), "UNAUTHORIZED_SENDER");
_;
}
constructor(address _owner, IERC20 _fundsToken)
public
ClaimsToken(_owner)
{
require(address(_fundsToken) != address(0));
fundsToken = _fundsToken;
}
/**
* @dev Withdraws available funds for user.
*/
function withdrawFunds()
external
payable
{
require(msg.value == 0, "");
uint256 withdrawableFunds = _prepareWithdraw();
require(fundsToken.transfer(msg.sender, withdrawableFunds), "TRANSFER_FAILED");
}
/**
* @dev For ERC223.
* Calls _registerFunds(), whereby total received funds (cumulative) gets updated.
* @param _sender Sender of tokens
* @param _value Amount of tokens
*/
function tokenFallback(address _sender, uint256 _value, bytes memory)
public
onlyFundsToken()
{
if (_value > 0) {
_registerFunds(_value);
emit FundsReceived(_sender, _value);
}
}
}
// File contracts/tokens/Token.sol
pragma solidity ^0.5.2;
contract Token is ERC20 {
bool transfersEnabled = true;
bool transfersReturningFalse = false;
constructor(uint256 supply) public {
_mint(msg.sender, supply);
}
function updateTransfersEnabled(bool enabled) external {
transfersEnabled = enabled;
}
function updateTransfersReturningFalse(bool enabled) external {
transfersReturningFalse = enabled;
}
function transfer(address to, uint256 value) public returns (bool) {
if (transfersReturningFalse) {
return false;
}
require(transfersEnabled);
return super.transfer(to, value);
}
}
{
"compilationTarget": {
"Moloch.sol": "Moloch"
},
"evmVersion": "petersburg",
"libraries": {},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_summoner","type":"address"},{"internalType":"address[]","name":"_approvedTokens","type":"address[]"},{"internalType":"uint256","name":"_periodDuration","type":"uint256"},{"internalType":"uint256","name":"_votingPeriodLength","type":"uint256"},{"internalType":"uint256","name":"_gracePeriodLength","type":"uint256"},{"internalType":"uint256","name":"_proposalDeposit","type":"uint256"},{"internalType":"uint256","name":"_dilutionBound","type":"uint256"},{"internalType":"uint256","name":"_processingReward","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"proposalId","type":"uint256"},{"indexed":false,"internalType":"address","name":"applicantAddress","type":"address"}],"name":"CancelProposal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"proposalIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"proposalId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"didPass","type":"bool"}],"name":"ProcessGuildKickProposal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"proposalIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"proposalId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"didPass","type":"bool"}],"name":"ProcessProposal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"proposalIndex","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"proposalId","type":"uint256"},{"indexed":false,"internalType":"bool","name":"didPass","type":"bool"}],"name":"ProcessWhitelistProposal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"memberAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"sharesToBurn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lootToBurn","type":"uint256"}],"name":"Ragequit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"delegateKey","type":"address"},{"indexed":true,"internalType":"address","name":"memberAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"proposalId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"proposalIndex","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"startingPeriod","type":"uint256"}],"name":"SponsorProposal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"applicant","type":"address"},{"indexed":false,"internalType":"uint256","name":"sharesRequested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lootRequested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"tributeOffered","type":"uint256"},{"indexed":false,"internalType":"address","name":"tributeToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"paymentRequested","type":"uint256"},{"indexed":false,"internalType":"address","name":"paymentToken","type":"address"},{"indexed":false,"internalType":"string","name":"details","type":"string"},{"indexed":false,"internalType":"bool[6]","name":"flags","type":"bool[6]"},{"indexed":false,"internalType":"uint256","name":"proposalId","type":"uint256"},{"indexed":true,"internalType":"address","name":"delegateKey","type":"address"},{"indexed":true,"internalType":"address","name":"memberAddress","type":"address"}],"name":"SubmitProposal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"proposalId","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"proposalIndex","type":"uint256"},{"indexed":true,"internalType":"address","name":"delegateKey","type":"address"},{"indexed":true,"internalType":"address","name":"memberAddress","type":"address"},{"indexed":false,"internalType":"uint8","name":"uintVote","type":"uint8"}],"name":"SubmitVote","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"summoner","type":"address"},{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256","name":"summoningTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"periodDuration","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"votingPeriodLength","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"gracePeriodLength","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"proposalDeposit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dilutionBound","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"processingReward","type":"uint256"}],"name":"SummonComplete","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountToCollect","type":"uint256"}],"name":"TokensCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"memberAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newDelegateKey","type":"address"}],"name":"UpdateDelegateKey","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"memberAddress","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdraw","type":"event"},{"constant":true,"inputs":[],"name":"ESCROW","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"GUILD","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"TOTAL","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"approvedTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"highestIndexYesVote","type":"uint256"}],"name":"canRagequit","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"cancelProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"collectTokens","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"depositToken","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"dilutionBound","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getCurrentPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"memberAddress","type":"address"},{"internalType":"uint256","name":"proposalIndex","type":"uint256"}],"name":"getMemberProposalVote","outputs":[{"internalType":"enum Moloch.Vote","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"getProposalFlags","outputs":[{"internalType":"bool[6]","name":"","type":"bool[6]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getProposalQueueLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getTokenCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"address","name":"token","type":"address"}],"name":"getUserTokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"gracePeriodLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"startingPeriod","type":"uint256"}],"name":"hasVotingPeriodExpired","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"memberAddressByDelegateKey","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"members","outputs":[{"internalType":"address","name":"delegateKey","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"uint256","name":"loot","type":"uint256"},{"internalType":"bool","name":"exists","type":"bool"},{"internalType":"uint256","name":"highestIndexYesVote","type":"uint256"},{"internalType":"uint256","name":"jailed","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"periodDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"proposalIndex","type":"uint256"}],"name":"processGuildKickProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"proposalIndex","type":"uint256"}],"name":"processProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"proposalIndex","type":"uint256"}],"name":"processWhitelistProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"processingReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposalCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"proposalDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"proposalQueue","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"proposals","outputs":[{"internalType":"address","name":"applicant","type":"address"},{"internalType":"address","name":"proposer","type":"address"},{"internalType":"address","name":"sponsor","type":"address"},{"internalType":"uint256","name":"sharesRequested","type":"uint256"},{"internalType":"uint256","name":"lootRequested","type":"uint256"},{"internalType":"uint256","name":"tributeOffered","type":"uint256"},{"internalType":"address","name":"tributeToken","type":"address"},{"internalType":"uint256","name":"paymentRequested","type":"uint256"},{"internalType":"address","name":"paymentToken","type":"address"},{"internalType":"uint256","name":"startingPeriod","type":"uint256"},{"internalType":"uint256","name":"yesVotes","type":"uint256"},{"internalType":"uint256","name":"noVotes","type":"uint256"},{"internalType":"string","name":"details","type":"string"},{"internalType":"uint256","name":"maxTotalSharesAndLootAtYesVote","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"proposedToKick","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"proposedToWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"memberToKick","type":"address"}],"name":"ragekick","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"sharesToBurn","type":"uint256"},{"internalType":"uint256","name":"lootToBurn","type":"uint256"}],"name":"ragequit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"name":"sponsorProposal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"memberToKick","type":"address"},{"internalType":"string","name":"details","type":"string"}],"name":"submitGuildKickProposal","outputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"applicant","type":"address"},{"internalType":"uint256","name":"sharesRequested","type":"uint256"},{"internalType":"uint256","name":"lootRequested","type":"uint256"},{"internalType":"uint256","name":"tributeOffered","type":"uint256"},{"internalType":"address","name":"tributeToken","type":"address"},{"internalType":"uint256","name":"paymentRequested","type":"uint256"},{"internalType":"address","name":"paymentToken","type":"address"},{"internalType":"string","name":"details","type":"string"}],"name":"submitProposal","outputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"proposalIndex","type":"uint256"},{"internalType":"uint8","name":"uintVote","type":"uint8"}],"name":"submitVote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"tokenToWhitelist","type":"address"},{"internalType":"string","name":"details","type":"string"}],"name":"submitWhitelistProposal","outputs":[{"internalType":"uint256","name":"proposalId","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"summoningTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalGuildBankTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalLoot","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newDelegateKey","type":"address"}],"name":"updateDelegateKey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"userTokenBalances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"votingPeriodLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawBalance","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"bool","name":"max","type":"bool"}],"name":"withdrawBalances","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]