diff --git a/destileria2.sol b/destileria2.sol index d5216826d7824f325271353588cbc746802f25b7..00fddb5e7e04e8d27f5e93f24dc05f244d8af8ab 100644 --- a/destileria2.sol +++ b/destileria2.sol @@ -15,7 +15,7 @@ pragma solidity >=0.4; contract destileria2 { - enum UserType { UserNotFound, UserOwner, UserDistributor, UserBeneficiary } + enum UserType { NotFound, Owner, Distributor, Beneficiary } // no hace falte 'indexed' ya que solamente va a haber // un evento de este tipo event deployed(address creator); @@ -40,7 +40,8 @@ contract destileria2 // Used for reclaiming unused positions in the userIndex uint256[] public deletedEntries; // Last time we did replenishAll, where did we stop? - uint256 allstopmarker = 0; + uint256 public allstopmarker = 0; + mapping(UserType => string) public userTypeNames; constructor() payable { @@ -61,10 +62,14 @@ contract destileria2 userIndex.push(UserEntry({ // This entry can be returned when no matches were found addr: address(0), - userType: UserType.UserNotFound, + userType: UserType.NotFound, topUpLimit: 0, deleted: true })); + userTypeNames[UserType.NotFound] = "User Not Found"; + userTypeNames[UserType.Owner] = "Owner"; + userTypeNames[UserType.Distributor] = "Distributor"; + userTypeNames[UserType.Beneficiary] = "Beneficiary"; } receive() external payable {} @@ -77,167 +82,152 @@ contract destileria2 modifier onlyDistributorOrOwner { - require(contains(UserType.UserDistributor, msg.sender) || msg.sender == owner); + require(contains(UserType.Distributor, msg.sender) || msg.sender == owner); _; } - function addBeneficiary(address addr, uint256 topuplimit) public onlyDistributorOrOwner returns(bool) + function addDistributor(address addr, uint256 topuplimit) public onlyOwner { - if (topuplimit == 0) - return false; - // User must not exist already - if (getUserType(addr) != UserType.UserNotFound) - return false; - if (topuplimit > getTopUpLimit(UserType.UserDistributor, msg.sender)) - return false; - emit added(msg.sender, addr, UserType.UserBeneficiary, topuplimit); - return !insert(UserType.UserBeneficiary, addr, topuplimit); + require(topuplimit > 0, "The topuplimit must be a positive number"); + require(getUserType(addr) == UserType.NotFound, "The distributor to add must not already be a listed user of any type."); + insert(UserType.Distributor, addr, topuplimit); + emit added(msg.sender, addr, UserType.Distributor, topuplimit); } - function addDistributor(address addr, uint256 topuplimit) public onlyOwner returns(bool) + function addBeneficiary(address addr, uint256 topuplimit) public onlyDistributorOrOwner { - if (topuplimit == 0) - return false; - // User must not exist already - if (getUserType(addr) != UserType.UserNotFound) - return false; - emit added(msg.sender, addr, UserType.UserDistributor, topuplimit); - return !insert(UserType.UserDistributor, addr, topuplimit); + require(topuplimit > 0, "topuplimit must be a positive integer."); + require(getUserType(addr) == UserType.NotFound, "The distributor to add must not already be a listed user of any type."); + insert(UserType.Beneficiary, addr, topuplimit); + emit added(msg.sender, addr, UserType.Beneficiary, topuplimit); } - function kick(UserType victimtype, address addr) private returns(bool) + function kick(UserType victimtype, address addr) private { - if (!contains(victimtype, addr)) - return false; + require(contains(victimtype, addr), "Address unknown."); + remove(victimtype, addr); emit kicked(msg.sender, addr); - return remove(victimtype, addr); } - function kickDistributor(address addr) public onlyOwner returns(bool) + function kickDistributor(address addr) public onlyOwner { - return kick(UserType.UserDistributor, addr); + kick(UserType.Distributor, addr); } - function kickBeneficiary(address addr) public onlyDistributorOrOwner returns(bool) + function kickBeneficiary(address addr) public onlyDistributorOrOwner { - return kick(UserType.UserBeneficiary, addr); + kick(UserType.Beneficiary, addr); } - function changeLimit(address addr, uint256 topuplimit) public onlyDistributorOrOwner returns(bool) + function changeLimit(address addr, uint256 topuplimit) public onlyDistributorOrOwner { uint256 pos = addressToUserEntryIndex[addr]; - if (pos == 0) - return false; + require(pos == 0, "Address unknown."); UserEntry memory entry = userIndex[pos]; - if (entry.userType != UserType.UserDistributor && entry.userType != UserType.UserBeneficiary) - return false; + require(entry.userType == UserType.Distributor || entry.userType == UserType.Beneficiary, "Only Beneficiaries and Distributors have limits associated."); if (msg.sender != owner) { // Only our owner can change limits for Distributors - if (entry.userType == UserType.UserDistributor) - return false; + require(entry.userType == UserType.Distributor, "Only the contract owner can changed limits for Distriburs."); // Distributors must obey the limit - if (topuplimit > getTopUpLimit(UserType.UserDistributor, msg.sender)) - return false; + require(topuplimit > getTopUpLimit(UserType.Distributor, msg.sender), "Distributors can not set a limit to exceed its own limit."); } // all is fine - now we change the limit userIndex[pos].topUpLimit = topuplimit; emit limitChanged(msg.sender, addr, topuplimit); - return true; } - function replenish(address victim, uint256 amount) public payable onlyDistributorOrOwner returns(bool) + function replenish(address victim, uint256 amount) public payable onlyDistributorOrOwner { - if (getUserType(victim) != UserType.UserBeneficiary) - return false; - uint256 topuplimit = getTopUpLimit(UserType.UserBeneficiary, victim); + require(amount > 0, "The topup amount must be a positive integer."); + require(getUserType(victim) == UserType.Beneficiary, "Address is not listed as beneficiary."); + uint256 topuplimit = getTopUpLimit(UserType.Beneficiary, victim); // Does the destination account have less than his limit? - if (victim.balance + amount > topuplimit) - return false; + require(victim.balance + amount <= topuplimit, "Desired topup will make the destination address exceed his topup limit."); // avoid uint256 overflow - if (victim.balance + amount < victim.balance) - return false; + require(victim.balance + amount > victim.balance, "Giving that amount, will make his balance overflow."); // Do we have enough ether to send? - if (address(this).balance < amount) - return false; + require(address(this).balance >= amount, "The contract does not have enough balance to send that amount."); // Do the transfer and emit an event address payable addrpayable = address(uint160(victim)); addrpayable.transfer(amount); emit replenished(msg.sender, victim, amount); - return true; } // To use this function, you should set gasLimit to at least gasEstimate + 50000 - function replenishList(address[] calldata accs, uint256[] calldata amounts) public payable + function replenishList(address[] calldata accs, uint256[] calldata amounts) public payable onlyDistributorOrOwner { - require(accs.length == amounts.length); + require(accs.length == amounts.length, "The two lists given as parameters are not of the same length."); uint256 i = 0; - while (i < accs.length && gasleft()>49999) + while (i < accs.length && gasleft() >= 32768) { // we don't like overflow if (accs[i].balance + amounts[i] < accs[i].balance) continue; // Don't allow amounts which will make the balance go above the top up limit. - if (accs[i].balance + amounts[i] < getTopUpLimit(UserType.UserBeneficiary, accs[i])) + if (accs[i].balance + amounts[i] < getTopUpLimit(UserType.Beneficiary, accs[i])) replenish(accs[i], amounts[i]); i++; } } // Pay to as many as possible, until we run out of ether (or run through the list - function replenishAll() public payable onlyDistributorOrOwner returns(bool) + function replenishAll() public payable onlyDistributorOrOwner { - if (userIndex.length == 0) - return false; + require(userIndex.length > 0, "There are no users registered."); + require(address(this).balance > 0, "We need wei, in order for us to send to others. Our balance is zero."); // The index may have shrunk under the stopmarker if (allstopmarker >= userIndex.length) allstopmarker = 0; // The distributor's topuplimit (for owner this is ignored) - uint256 distlimit = getTopUpLimit(UserType.UserDistributor, msg.sender); + uint256 distlimit = getTopUpLimit(UserType.Distributor, msg.sender); uint256 i = allstopmarker; - while (++i != allstopmarker) + // just a round number + while (gasleft()>=32768) { + i++; // If we get to the end of the list, loop back to the start of it if (i >= userIndex.length) i = 0; UserEntry memory entry = userIndex[i]; // make sure we skip deleted entries - if (entry.deleted) - continue; // We only handle Beneficiaries - if (entry.userType != UserType.UserBeneficiary) - continue; - // - uint256 topuplimit; - if (msg.sender == owner || entry.topUpLimit <= distlimit) - topuplimit = entry.topUpLimit; - else - topuplimit = distlimit; - // Does the user actually need to get topped up? - if (topuplimit > entry.addr.balance) - continue; - // What's the difference? - uint256 amount = topuplimit - entry.addr.balance; - replenish(entry.addr, amount); - // Do we have enough ether to send? - if (address(this).balance < amount) - continue; - // Do the transfer and emit an event - address payable addrpayable = address(uint160(entry.addr)); - addrpayable.transfer(amount); - emit replenished(msg.sender, entry.addr, amount); + if (!entry.deleted && entry.userType == UserType.Beneficiary) + { + uint256 topuplimit = distlimit; + if (msg.sender == owner || entry.topUpLimit <= distlimit) + topuplimit = entry.topUpLimit; + // Fill up if he is below 75% of his topuplimit + if (entry.addr.balance/100*75 < topuplimit) + { + // What's the difference? + uint256 amount = topuplimit - entry.addr.balance; + // Do we have enough ether to send? + if (amount > address(this).balance) + amount = address(this).balance; + // Do the transfer and emit an event + address payable addrpayable = address(uint160(entry.addr)); + addrpayable.transfer(amount); + emit replenished(msg.sender, entry.addr, amount); + } + } + // Only run through the list once + if ( i == allstopmarker ) + break; + if (address(this).balance == 0) + break; } - return true; + return; } function getDistributorLimit(address addr) public view returns(uint256) { - return getTopUpLimit(UserType.UserDistributor, addr); + return getTopUpLimit(UserType.Distributor, addr); } function getBeneficiaryLimit(address addr) public view returns(uint256) { - return getTopUpLimit(UserType.UserBeneficiary, addr); + return getTopUpLimit(UserType.Beneficiary, addr); } function getBeneficiariesCount() public view returns(uint256 count) @@ -248,64 +238,60 @@ contract destileria2 { if (userIndex[i].deleted) continue; - if (userIndex[i].userType == UserType.UserBeneficiary) + if (userIndex[i].userType == UserType.Beneficiary) count++; } } + function getUserIndexCount() public view returns(uint256) + { + return userIndex.length; + } + function isBeneficiary(address addr) public view returns(bool) { - return contains(UserType.UserBeneficiary, addr); + return contains(UserType.Beneficiary, addr); } function isDistributor(address addr) public view returns(bool) { - return contains(UserType.UserDistributor, addr); + return contains(UserType.Distributor, addr); } - function insert(UserType victimtype, address addr, uint256 value) private returns(bool replaced) + function insert(UserType victimtype, address addr, uint256 value) private { uint256 pos = addressToUserEntryIndex[addr]; if (pos > 0) + assert( userIndex[pos].addr != addr || userIndex[pos].deleted ); + if (deletedEntries.length > 0) { - assert(userIndex[pos].addr == addr); - assert(!userIndex[pos].deleted); - replaced = true; + // recycle deleted entries, if possible + pos = deletedEntries[deletedEntries.length-1]; + deletedEntries.pop(); } else { - replaced = false; - if (deletedEntries.length > 0) - { - // recycle deleted entries, if possible - pos = deletedEntries[deletedEntries.length-1]; - deletedEntries.pop(); - } - else - { - pos = userIndex.length; - userIndex.push(); - } + pos = userIndex.length; + userIndex.push(); } + addressToUserEntryIndex[addr] = pos; userIndex[pos].addr = addr; userIndex[pos].userType = victimtype; userIndex[pos].topUpLimit = value; userIndex[pos].deleted = false; - if (!replaced) - addressToUserEntryIndex[addr] = pos; } - function remove(UserType victimtype, address addr) private returns(bool success) + function remove(UserType victimtype, address addr) private { uint256 pos = addressToUserEntryIndex[addr]; - if (pos == 0) - return false; - if(victimtype != userIndex[pos].userType) - return false; + assert(pos>0); + assert(userIndex[pos].addr == addr); + assert(userIndex[pos].userType == victimtype); + assert(!userIndex[pos].deleted); + userIndex[pos].addr = address(0); + userIndex[pos].deleted = true; delete addressToUserEntryIndex[addr]; deletedEntries.push(pos); - userIndex[pos].deleted = true; - return true; } function getEntry(address addr) private view returns(UserEntry memory) @@ -318,7 +304,7 @@ contract destileria2 function getUserType(address addr) public view returns(UserType) { uint256 pos = addressToUserEntryIndex[addr]; - // no match will give the 0th userIndex entry which has UserNotFound as UserType + // no match will give the 0th userIndex entry which has NotFound as UserType return userIndex[pos].userType; } @@ -337,4 +323,8 @@ contract destileria2 return true; return false; } + + function kill() public onlyOwner { + selfdestruct(msg.sender); + } }