contract SparksterTokenSwap is StandardToken, multiowned {
	using SafeMath for uint256;
	struct Member {
		mapping(uint256 => uint256) weiBalance; // How much wei has this member contributed for this group?
		uint256[] groups; // A list of groups this member belongs to.
		// Used to see how many locked tokens this user has so we don't have to iterate over all groups;
		// we can just iterate over the groups this user belongs to.
	}

	enum GroupStates {
		none,
		distributing,
		distributed,
		unlocked // This value is only set for groups that don't define an unlock time.
		// For groups that define an unlock time, the group is unlocked if Group.state == GroupStates.distributed and the unlock time has elapsed; the GroupStates.unlocked state will be ignored.
	}

	struct Group {
		GroupStates state; // Indicates whether the group is distributed or unlocked.
		mapping(address => bool) exists; // If exists[address] is true, this address has made a purchase on this group before.
		string name;
		uint256 ratio; // 1 eth:ratio tokens. This amount represents the decimal amount. ratio*10**decimal = ratio sparks.
		uint256 unlockTime; // The timestamp after which tokens in this group are unlocked.
		// If set to a truthy value, the group will be considered unlocked after this time elapses and the group is distributed.
		uint256 startTime; // Epoch of crowdsale start time.
		uint256 phase1endTime; // Epoch of phase1 end time.
		uint256 phase2endTime; // Epoch of phase2 end time.
		uint256 deadline; // No contributions allowed after this epoch.
		uint256 max2; // cap of phase2
		uint256 max3; // Total ether this group can collect in phase 3.
		uint256 weiTotal; // How much ether has this group collected?
		uint256 cap; // The hard ether cap.
		uint256 nextDistributionIndex; // The next index to start distributing at.
		address[] addresses; // List of addresses that have made a purchase on this group.
	}

	address[] internal initialSigners = [0xCdF06E2F49F7445098CFA54F52C7f43eE40fa016, 0x8a26B989880CB06d18411C0Cd9C3fa960FDACA51, 0x65580BB13A6edF674B5a25A3c3c223471f5f3706];
	address public oracleAddress;
	address constant public oldSprkAddress = 0x971d048E737619884f2df75e31c7Eb6412392328;
	address public owner; // We call this the owner simply because so many ERC-20 contracts have an owner variable,
	// so if an API implements getting the balance of the original token holder by querying balanceOf(owner()), this contract won't break the API.
	// But in our implementation, the contract is multiowned so this owner field has no owning significance besides being the token generator.
	bool public transferLock = true; // A Global transfer lock. Set to lock down all tokens from all groups.
	bool public allowedToBuyBack = false;
	bool public allowedToPurchase = false;
	string public name;									 // name for display
	string public symbol;								 //An identifier
	uint8 public decimals;							//How many decimals to show.
	uint8 constant internal maxGroups = 100; // Total number of groups we are allowed to create.
	uint256 public penalty;
	uint256 public maxGasPrice; // The maximum allowed gas for the purchase function.
	uint256 internal nextGroupNumber;
	uint256 public sellPrice; // sellPrice wei:1 spark token; we won't allow to sell back parts of a token.
	uint256 public minimumRequiredBalance; // How much wei is required for the contract to hold that will cover all refunds.
	// Owners must leave this much in the contract.
	uint256 public openGroupNumber;
	mapping(address => Member) internal members; // For reverse member lookup.
	mapping(uint256 => Group) internal groups; // For reverse group lookup.
	mapping(address => uint256) internal withdrawableBalances; // How much wei is this address allowed to withdraw?
	ERC20 oldSprk; // The old Sparkster token contract
	event WantsToPurchase(address walletAddress, uint256 weiAmount, uint256 groupNumber, bool inPhase1);
	event PurchasedCallbackOnAccept(uint256 groupNumber, address[] addresses);
	event WantsToDistribute(uint256 groupNumber);
	event NearingHardCap(uint256 groupNumber, uint256 remainder);
	event ReachedHardCap(uint256 groupNumber);
	event DistributeDone(uint256 groupNumber);
	event DistributedBatch(uint256 groupNumber, uint256 howMany);
	event ShouldCallDoneDistributing();
	event AirdroppedBatch(address[] addresses);
	event RefundedBatch(address[] addresses);
	event AddToGroup(address walletAddress, uint256 groupNumber);
	event ChangedTransferLock(bool transferLock);
	event ChangedAllowedToPurchase(bool allowedToPurchase);
	event ChangedAllowedToBuyBack(bool allowedToBuyBack);
	event SetSellPrice(uint256 sellPrice);

	modifier onlyOwnerOrOracle() {
		require(isOwner(msg.sender) || msg.sender == oracleAddress, "Auth");
		_;
	}

	modifier onlyManyOwnersOrOracle(bytes32 _operation) {
		if (confirmAndCheck(_operation) || msg.sender == oracleAddress)
			_;
		// Don't throw here since confirmAndCheck needs to preserve state.
	}

	modifier canTransfer() {
		if (!isOwner(msg.sender)) { // Owners are immuned from the transfer lock.
			require(!transferLock, "Locked");
		}
		_;
	}

	modifier canPurchase() {
		require(allowedToPurchase, "Disallowed");
		_;
	}

	modifier canSell() {
		require(allowedToBuyBack, "Denied");
		_;
	}

	function() external payable {
		purchase();
	}

	constructor()
	multiowned( initialSigners, 2) public {
		require(isOwner(msg.sender), "NaO");
		oldSprk = ERC20(oldSprkAddress);
		owner = msg.sender;
		name = "Sparkster";									// Set the name for display purposes
		decimals = 18;					 // Amount of decimals for display purposes
		symbol = "SPRK";							// Set the symbol for display purposes
		maxGasPrice = 40 * 10**9; // Set max gas to 40 Gwei to start with.
		uint256 amount = 435000000;
		uint256 decimalAmount = amount.mul(uint(10)**decimals);
		totalSupply_ = decimalAmount;
		balances[msg.sender] = decimalAmount;
		emit Transfer(address(0), msg.sender, decimalAmount); // Per erc20 standards-compliance.
	}

	function swapTokens() public canTransfer returns(bool) {
		require(msg.sender != address(this), "Self"); // Don't let this contract swap tokens with itself.
		// First, find out how much the sender has allowed us to spend. This is the amount of Sprk we're allowed to move.
		// Sender can set this value by calling increaseApproval on the old contract.
		uint256 amountToTransfer = oldSprk.allowance(msg.sender, address(this));
		require(amountToTransfer > 0, "Amount==0");
		// However many tokens we took away from the user in the old contract, give that amount in our new contract.
		balances[msg.sender] = balances[msg.sender].add(amountToTransfer);
		balances[owner] = balances[owner].sub(amountToTransfer);
		// Finally, transfer the old tokens away from the sender and to our address. This will effectively freeze the tokens because no one can transact on this contract's behalf except the contract.
		require(oldSprk.transferFrom(msg.sender, address(this), amountToTransfer), "Transfer");
		emit Transfer(owner, msg.sender, amountToTransfer);
		return true;
	}

	function setOwnerAddress(address newAddress) public onlyManyOwners(keccak256(msg.data)) returns(bool) {
		require(newAddress != address(0), "Invl");
		require(newAddress != owner, "Self");
		uint256 oldOwnerBalance = balances[owner];
		balances[newAddress] = balances[newAddress].add(oldOwnerBalance);
		balances[owner] = 0;
		emit Transfer(owner, newAddress, oldOwnerBalance);
		owner = newAddress;
		return true;
	}

	function setOracleAddress(address newAddress) public onlyManyOwners(keccak256(msg.data)) returns(bool) {
		oracleAddress = newAddress;
		return true;
	}

	function removeOracleAddress() public onlyOwner returns(bool) {
		oracleAddress = address(0);
		return true;
	}

	function setMaximumGasPrice(uint256 gweiPrice) public onlyManyOwners(keccak256(msg.data)) returns(bool) {
		maxGasPrice = gweiPrice.mul(10**9); // Convert the gwei value to wei.
		return true;
	}

	function purchase() public canPurchase payable returns(bool) {
		Member storage memberRecord = members[msg.sender];
		Group storage openGroup = groups[openGroupNumber];
		require(openGroup.ratio > 0, "Not initialized"); // Group must be initialized.
		uint256 currentTimestamp = block.timestamp;
		require(currentTimestamp >= openGroup.startTime && currentTimestamp <= openGroup.deadline, "OOR");
		// The timestamp must be greater than or equal to the start time and less than or equal to the deadline time
		require(openGroup.state == GroupStates.none, "State");
		// Don't allow to purchase if we're in the middle of distributing, have already distributed, or have unlocked.
		require(tx.gasprice <= maxGasPrice, "Gas price"); // Restrict maximum gas this transaction is allowed to consume.
		uint256 weiAmount = msg.value;																		// The amount purchased by the current member
		// Updated in purchaseCallbackOnAccept.
		require(weiAmount >= 0.1 ether, "Amount<0.1 ether");
		uint256 weiTotal = openGroup.weiTotal.add(weiAmount); // Calculate total contribution of all members in this group.
		// WeiTotals are updated in purchaseCallbackOnAccept
		require(weiTotal <= openGroup.cap, "Cap exceeded");														// Check to see if accepting these funds will put us above the hard ether cap.
		uint256 userWeiTotal = memberRecord.weiBalance[openGroupNumber].add(weiAmount); // Calculate the total amount purchased by the current member
		if (!openGroup.exists[msg.sender]) { // Has this person not purchased on this group before?
			openGroup.addresses.push(msg.sender);
			openGroup.exists[msg.sender] = true;
			memberRecord.groups.push(openGroupNumber);
		}
		if(currentTimestamp <= openGroup.phase1endTime){																			 // whether the current timestamp is in the first phase
			emit WantsToPurchase(msg.sender, weiAmount, openGroupNumber, true);
			return true;
		} else if (currentTimestamp <= openGroup.phase2endTime) { // Are we in phase 2?
			require(userWeiTotal <= openGroup.max2, "Phase2 cap exceeded"); // Allow to contribute no more than max2 in phase 2.
			emit WantsToPurchase(msg.sender, weiAmount, openGroupNumber, false);
			return true;
		} else { // We've passed both phases 1 and 2.
			require(userWeiTotal <= openGroup.max3, "Phase3 cap exceeded"); // Don't allow to contribute more than max3 in phase 3.
			emit WantsToPurchase(msg.sender, weiAmount, openGroupNumber, false);
			return true;
		}
	}

	function purchaseCallbackOnAccept(
		uint256 groupNumber, address[] memory addresses, uint256[] memory weiAmounts)
	public onlyManyOwnersOrOracle(keccak256(msg.data)) returns(bool success) {
		return accept(groupNumber, addresses, weiAmounts);
	}

	// Base function for accepts.
	// Calling functions should be multisig.
	function accept(
		uint256 groupNumber, address[] memory addresses, uint256[] memory weiAmounts)
	private onlyOwnerOrOracle returns(bool) {
		uint256 n = addresses.length;
		require(n == weiAmounts.length, "Length");
		Group storage theGroup = groups[groupNumber];
		uint256 weiTotal = theGroup.weiTotal;
		for (uint256 i = 0; i < n; i++) {
			Member storage memberRecord = members[addresses[i]];
			uint256 weiAmount = weiAmounts[i];
			weiTotal = weiTotal.add(weiAmount);								 // Calculate the total amount purchased by all members in this group.
			memberRecord.weiBalance[groupNumber] = memberRecord.weiBalance[groupNumber].add(weiAmount);
			// Record the total amount purchased by the current member
		}
		theGroup.weiTotal = weiTotal;
		if (getHowMuchUntilHardCap_(groupNumber) <= 100 ether) {
			emit NearingHardCap(groupNumber, getHowMuchUntilHardCap_(groupNumber));
			if (weiTotal >= theGroup.cap) {
				emit ReachedHardCap(groupNumber);
			}
		}
		emit PurchasedCallbackOnAccept(groupNumber, addresses);
		return true;
	}

	function insertAndApprove(uint256 groupNumber, address[] memory addresses, uint256[] memory weiAmounts)
	public onlyManyOwnersOrOracle(keccak256(msg.data)) returns(bool) {
		uint256 n = addresses.length;
		require(n == weiAmounts.length, "Length");
		Group storage theGroup = groups[groupNumber];
		for (uint256 i = 0; i < n; i++) {
			address theAddress = addresses[i];
			if (!theGroup.exists[theAddress]) {
				theGroup.addresses.push(theAddress);
				theGroup.exists[theAddress] = true;
				members[theAddress].groups.push(groupNumber);
			}
		}
		return accept(groupNumber, addresses, weiAmounts);
	}

	function callbackInsertApproveAndDistribute(
		uint256 groupNumber, address[] memory addresses, uint256[] memory weiAmounts)
	public onlyManyOwnersOrOracle(keccak256(msg.data)) returns(bool) {
		uint256 n = addresses.length;
		require(n == weiAmounts.length, "Length");
		require(getGroupState(groupNumber) != GroupStates.unlocked, "Unlocked");
		Group storage theGroup = groups[groupNumber];
		uint256 newOwnerSupply = balances[owner];
		for (uint256 i = 0; i < n; i++) {
			address theAddress = addresses[i];
			Member storage memberRecord = members[theAddress];
			uint256 weiAmount = weiAmounts[i];
			memberRecord.weiBalance[groupNumber] = memberRecord.weiBalance[groupNumber].add(weiAmount);
			// Record the total amount purchased by the current member
			if (!theGroup.exists[theAddress]) {
				theGroup.addresses.push(theAddress);
				theGroup.exists[theAddress] = true;
				memberRecord.groups.push(groupNumber);
			}
			uint256 additionalBalance = weiAmount.mul(theGroup.ratio); // Don't give cumulative tokens; one address can be distributed multiple times.
			if (additionalBalance > 0) {
				balances[theAddress] = balances[theAddress].add(additionalBalance);
				newOwnerSupply = newOwnerSupply.sub(additionalBalance); // Update the available number of tokens.
				emit Transfer(owner, theAddress, additionalBalance); // Notify exchanges of the distribution.
			}
		}
		balances[owner] = newOwnerSupply;
		emit PurchasedCallbackOnAccept(groupNumber, addresses);
		if (getGroupState(groupNumber) != GroupStates.distributed)
			theGroup.state = GroupStates.distributed;
		return true;
	}

	function getWithdrawableAmount() public view returns(uint256) {
		return withdrawableBalances[msg.sender];
	}

	function withdraw() public returns (bool) {
		uint256 amount = withdrawableBalances[msg.sender];
		require(amount > 0, "NSF");
		withdrawableBalances[msg.sender] = 0;
		minimumRequiredBalance = minimumRequiredBalance.sub(amount);
		msg.sender.transfer(amount);
		return true;
	}

	function refund(address[] memory addresses, uint256[] memory weiAmounts) public onlyManyOwners(keccak256(msg.data)) returns(bool success) {
		uint256 n = addresses.length;
		require (n == weiAmounts.length, "Length");
		uint256 thePenalty = penalty;
		uint256 totalRefund = 0;
		for(uint256 i = 0; i < n; i++) {
			uint256 weiAmount = weiAmounts[i];
			address payable theAddress = address(uint160(address(addresses[i])));
			if (thePenalty < weiAmount) {
				weiAmount = weiAmount.sub(thePenalty);
				totalRefund = totalRefund.add(weiAmount);
				withdrawableBalances[theAddress] = withdrawableBalances[theAddress].add(weiAmount);
			}
		}
		require(address(this).balance >= minimumRequiredBalance + totalRefund, "NSF"); // The contract must have enough to refund these addresses.
		minimumRequiredBalance = minimumRequiredBalance.add(totalRefund);
		emit RefundedBatch(addresses);
		return true;
	}

	function signalDoneDistributing(uint256 groupNumber) public onlyManyOwnersOrOracle(keccak256(msg.data)) {
		Group storage theGroup = groups[groupNumber];
		theGroup.state = GroupStates.distributed;
		emit DistributeDone(groupNumber);
	}

	function drain(address payable to) public onlyManyOwners(keccak256(msg.data)) returns(bool) {
		uint256 amountAllowedToDrain = address(this).balance.sub(minimumRequiredBalance);
		require(amountAllowedToDrain > 0, "NSF");
		to.transfer(amountAllowedToDrain);
		return true;
	}

	function setPenalty(uint256 newPenalty) public onlyManyOwners(keccak256(msg.data)) returns(bool) {
		penalty = newPenalty;
		return true;
	}

	function buyback(uint256 amount) public canSell {
		require(sellPrice>0, "sellPrice==0");
		uint256 decimalAmount = amount.mul(uint(10)**decimals); // convert the full token value to the smallest unit possible.
		require(balances[msg.sender].sub(decimalAmount) >= getLockedTokens_(msg.sender), "NSF"); // Don't allow to sell locked tokens.
		balances[msg.sender] = balances[msg.sender].sub(decimalAmount);
		// Amount is considered to be how many full tokens the user wants to sell.
		uint256 totalCost = amount.mul(sellPrice); // sellPrice is the per-full-token value.
		minimumRequiredBalance = minimumRequiredBalance.add(totalCost);
		require(address(this).balance >= minimumRequiredBalance, "NSF"); // The contract must have enough funds to cover the selling.
		balances[owner] = balances[owner].add(decimalAmount); // Put these tokens back into the available pile.
		withdrawableBalances[msg.sender] = withdrawableBalances[msg.sender].add(totalCost); // Pay the seller for their tokens.
		emit Transfer(msg.sender, owner, decimalAmount); // Notify exchanges of the sell.
	}

	function fundContract() public onlyOwnerOrOracle payable { // For the owner to put funds into the contract.
	}

	function setSellPrice(uint256 thePrice) public onlyManyOwners(keccak256(msg.data)) returns (bool) {
		sellPrice = thePrice;
		emit SetSellPrice(thePrice);
		return true;
	}

	function setAllowedToBuyBack(bool value) public onlyManyOwners(keccak256(msg.data)) {
		allowedToBuyBack = value;
		emit ChangedAllowedToBuyBack(value);
	}

	function setAllowedToPurchase(bool value) public onlyManyOwners(keccak256(msg.data)) returns(bool) {
		allowedToPurchase = value;
		emit ChangedAllowedToPurchase(value);
		return true;
	}

	function createGroup(
		string memory groupName, uint256 startEpoch, uint256 phase1endEpoch, uint256 phase2endEpoch, uint256 deadlineEpoch,
		uint256 unlockAfterEpoch, uint256 phase2weiCap, uint256 phase3weiCap, uint256 hardWeiCap, uint256 ratio) public
	onlyManyOwners(keccak256(msg.data)) returns (bool success, uint256 createdGroupNumber) {
		require(nextGroupNumber < maxGroups, "Too many groups");
		createdGroupNumber = nextGroupNumber;
		Group storage theGroup = groups[createdGroupNumber];
		theGroup.name = groupName;
		theGroup.startTime = startEpoch;
		theGroup.phase1endTime = phase1endEpoch;
		theGroup.phase2endTime = phase2endEpoch;
		theGroup.deadline = deadlineEpoch;
		theGroup.unlockTime = unlockAfterEpoch;
		theGroup.max2 = phase2weiCap;
		theGroup.max3 = phase3weiCap;
		theGroup.cap = hardWeiCap;
		theGroup.ratio = ratio;
		nextGroupNumber++;
		success = true;
	}

	function getGroup(uint256 groupNumber) public view returns(string memory groupName, string memory status, uint256 phase2cap,
	uint256 phase3cap, uint256 cap, uint256 ratio, uint256 startTime, uint256 phase1endTime, uint256 phase2endTime, uint256 deadline,
	uint256 weiTotal) {
		require(groupNumber < nextGroupNumber, "OOR");
		Group storage theGroup = groups[groupNumber];
		groupName = theGroup.name;
		GroupStates state = getGroupState(groupNumber);
		status = (state == GroupStates.none)? "none"
		:(state == GroupStates.distributing)? "distributing"
		:(state == GroupStates.distributed)? "distributed":"unlocked";
		phase2cap = theGroup.max2;
		phase3cap = theGroup.max3;
		cap = theGroup.cap;
		ratio = theGroup.ratio;
		startTime = theGroup.startTime;
		phase1endTime = theGroup.phase1endTime;
		phase2endTime = theGroup.phase2endTime;
		deadline = theGroup.deadline;
		weiTotal = theGroup.weiTotal;
	}

	function getGroupUnlockTime(uint256 groupNumber) public view returns(uint256) {
		require(groupNumber < nextGroupNumber, "OOR");
		Group storage theGroup = groups[groupNumber];
		return theGroup.unlockTime;
	}

	function getHowMuchUntilHardCap_(uint256 groupNumber) internal view returns(uint256) {
		Group storage theGroup = groups[groupNumber];
		if (theGroup.weiTotal > theGroup.cap) { // calling .sub in this situation will throw.
			return 0;
		}
		return theGroup.cap.sub(theGroup.weiTotal);
	}

	function getHowMuchUntilHardCap() public view returns(uint256) {
		return getHowMuchUntilHardCap_(openGroupNumber);
	}

	function addMemberToGroup(address walletAddress, uint256 groupNumber) public onlyOwner returns(bool) {
		emit AddToGroup(walletAddress, groupNumber);
		return true;
	}

	function instructOracleToDistribute(uint256 groupNumber) public onlyOwnerOrOracle returns(bool) {
		require(groupNumber < nextGroupNumber && getGroupState(groupNumber) < GroupStates.distributed, "Dist");
		emit WantsToDistribute(groupNumber);
		return true;
	}

	function distributeCallback(uint256 groupNumber, uint256 howMany) public onlyManyOwnersOrOracle(keccak256(msg.data)) returns (bool success) {
		Group storage theGroup = groups[groupNumber];
		GroupStates state = getGroupState(groupNumber);
		require(state < GroupStates.distributed, "Dist");
		if (state != GroupStates.distributing) {
			theGroup.state = GroupStates.distributing;
		}
		uint256 n = theGroup.addresses.length;
		uint256 nextDistributionIndex = theGroup.nextDistributionIndex;
		uint256 exclusiveEndIndex = nextDistributionIndex + howMany;
		if (exclusiveEndIndex > n) {
			exclusiveEndIndex = n;
		}
		uint256 newOwnerSupply = balances[owner];
		for (uint256 i = nextDistributionIndex; i < exclusiveEndIndex; i++) {
			address theAddress = theGroup.addresses[i];
			uint256 balance = getUndistributedBalanceOf_(theAddress, groupNumber);
			if (balance > 0) { // No need to waste ticks if they have no tokens to distribute
				balances[theAddress] = balances[theAddress].add(balance);
				newOwnerSupply = newOwnerSupply.sub(balance); // Update the available number of tokens.
				emit Transfer(owner, theAddress, balance); // Notify exchanges of the distribution.
			}
		}
		balances[owner] = newOwnerSupply;
		if (exclusiveEndIndex < n) {
			emit DistributedBatch(groupNumber, howMany);
		} else { // We've finished distributing people
			// However, signalDoneDistributing needs to be manually called since it's multisig. So if we're calling this function from multiple owners then calling signalDoneDistributing from here won't work.
			emit ShouldCallDoneDistributing();
		}
		theGroup.nextDistributionIndex = exclusiveEndIndex; // Usually not necessary if we've finished distribution,
		// but if we don't update this, getHowManyLeftToDistribute will never show 0.
		return true;
	}

	function getHowManyLeftToDistribute(uint256 groupNumber) public view returns(uint256 remainder) {
		Group storage theGroup = groups[groupNumber];
		return theGroup.addresses.length - theGroup.nextDistributionIndex;
	}

	function unlock(uint256 groupNumber) public onlyManyOwners(keccak256(msg.data)) returns (bool success) {
		Group storage theGroup = groups[groupNumber];
		require(getGroupState(groupNumber) == GroupStates.distributed, "Undist"); // Distribution must have occurred first.
		require(theGroup.unlockTime == 0, "Unlocktime");
		// If the group has set an explicit unlock time, the admins cannot force an unlock and the unlock will happen automatically.
		theGroup.state = GroupStates.unlocked;
		return true;
	}

	function liftGlobalLock() public onlyManyOwners(keccak256(msg.data)) returns(bool) {
		transferLock = false;
		emit ChangedTransferLock(transferLock);
		return true;
	}

	function airdrop( address[] memory addresses, uint256[] memory tokenDecimalAmounts) public onlyManyOwnersOrOracle(keccak256(msg.data))
	returns (bool) {
		uint256 n = addresses.length;
		require(n == tokenDecimalAmounts.length, "Length");
		uint256 newOwnerBalance = balances[owner];
		for (uint256 i = 0; i < n; i++) {
			address theAddress = addresses[i];
			uint256 airdropAmount = tokenDecimalAmounts[i];
			if (airdropAmount > 0) {
				uint256 currentBalance = balances[theAddress];
				balances[theAddress] = currentBalance.add(airdropAmount);
				newOwnerBalance = newOwnerBalance.sub(airdropAmount);
				emit Transfer(owner, theAddress, airdropAmount);
			}
		}
		balances[owner] = newOwnerBalance;
		emit AirdroppedBatch(addresses);
		return true;
	}

	function transfer(address _to, uint256 _value) public canTransfer returns (bool success) {
		// If the transferrer has purchased tokens, they must be unlocked before they can be used.
		require(balances[msg.sender].sub(_value) >= getLockedTokens_(msg.sender), "Not enough tokens");
		return super.transfer(_to, _value);
	}

	function transferFrom(address _from, address _to, uint256 _value) public canTransfer returns (bool success) {
		// If the transferrer has purchased tokens, they must be unlocked before they can be used.
		require(balances[_from].sub(_value) >= getLockedTokens_(_from), "Not enough tokens");
		return super.transferFrom(_from, _to, _value);
	}

	function setOpenGroup(uint256 groupNumber) public onlyManyOwners(keccak256(msg.data)) returns (bool) {
		require(groupNumber < nextGroupNumber, "OOR");
		openGroupNumber = groupNumber;
		return true;
	}

	function getGroupState(uint256 groupNumber) public view returns(GroupStates) {
		require(groupNumber < nextGroupNumber, "out of range"); // Must have created at least one group.
		Group storage theGroup = groups[groupNumber];
		if (theGroup.state < GroupStates.distributed)
			return theGroup.state;
		// Here, we have two cases.
		// If this is a time-based group, tokens will only unlock after a certain time. Otherwise, we depend on the group's state being set to unlock.
		if (block.timestamp < theGroup.unlockTime)
			return GroupStates.distributed;
		else if (theGroup.unlockTime > 0) // Here, blocktime exceeds the group unlock time, and we've set an unlock time explicitly
			return GroupStates.unlocked;
		return theGroup.state;
	}

	function getLockedTokensInGroup_(address walletAddress, uint256 groupNumber) internal view returns (uint256 balance) {
		Member storage theMember = members[walletAddress];
		if (getGroupState(groupNumber) == GroupStates.unlocked) {
			return 0;
		}
		return theMember.weiBalance[groupNumber].mul(groups[groupNumber].ratio);
	}

	function getLockedTokens_(address walletAddress) internal view returns(uint256 balance) {
		uint256[] storage memberGroups = members[walletAddress].groups;
		uint256 n = memberGroups.length;
		for (uint256 i = 0; i < n; i++) {
			balance = balance.add(getLockedTokensInGroup_(walletAddress, memberGroups[i]));
		}
		return balance;
	}

	function getLockedTokens(address walletAddress) public view returns(uint256 balance) {
		return getLockedTokens_(walletAddress);
	}

	function getUndistributedBalanceOf_(address walletAddress, uint256 groupNumber) internal view returns (uint256 balance) {
		Member storage theMember = members[walletAddress];
		Group storage theGroup = groups[groupNumber];
		if (getGroupState(groupNumber) > GroupStates.distributing) {
			return 0;
		}
		return theMember.weiBalance[groupNumber].mul(theGroup.ratio);
	}

	function getUndistributedBalanceOf(address walletAddress, uint256 groupNumber) public view returns (uint256 balance) {
		return getUndistributedBalanceOf_(walletAddress, groupNumber);
	}

	function checkMyUndistributedBalance(uint256 groupNumber) public view returns (uint256 balance) {
		return getUndistributedBalanceOf_(msg.sender, groupNumber);
	}
}
function accept(
	uint256 groupNumber, address[] memory addresses, uint256[] memory weiAmounts)
private onlyOwnerOrOracle returns(bool) {
	uint256 n = addresses.length;
	require(n == weiAmounts.length, "Length");
	Group storage theGroup = groups[groupNumber];
	uint256 weiTotal = theGroup.weiTotal;
	for (uint256 i = 0; i < n; i++) {
		Member storage memberRecord = members[addresses[i]];
		uint256 weiAmount = weiAmounts[i];
		weiTotal = weiTotal.add(weiAmount);								 // Calculate the total amount purchased by all members in this group.
		memberRecord.weiBalance[groupNumber] = memberRecord.weiBalance[groupNumber].add(weiAmount);
		// Record the total amount purchased by the current member
	}
	theGroup.weiTotal = weiTotal;
	if (getHowMuchUntilHardCap_(groupNumber) <= 100 ether) {
		emit NearingHardCap(groupNumber, getHowMuchUntilHardCap_(groupNumber));
		if (weiTotal >= theGroup.cap) {
			emit ReachedHardCap(groupNumber);
		}
	}
	emit PurchasedCallbackOnAccept(groupNumber, addresses);
	return true;
}

// Base function for accepts.
// Calling functions should be multisig.