diff --git a/src/Distillery.sol b/src/Distillery.sol index 21feb1da24fd32bd89fb9f69bc367ce97422f0bb..f37872381de25cacc93dca148483c6a71138234f 100644 --- a/src/Distillery.sol +++ b/src/Distillery.sol @@ -5,7 +5,7 @@ contract Distillery { address owner; struct Allowances { address beneficiary; - uint256 value; + uint topuplimit; } Allowances[] thelist; // We use distpos to remember where we were stopped processing last time @@ -16,7 +16,11 @@ contract Distillery { // that some would have first priority in getting ether at every invocation // So we continue checking the list from the same place where we left off // at the previous invocation of distribute() - uint256 distpos; + uint distpos; + + event distributeStartedBy( address activator ); + event setAllowance( address subject, uint amount ); + event xfrAllowance( address subject, uint amount ); constructor() public payable { owner = msg.sender; @@ -26,83 +30,107 @@ contract Distillery { _; } // Using this function, you can find out how long thelist is. - function numberOfBeneficiaries() public view returns (uint256) { + function numberOfBeneficiaries() public view returns ( uint ) { return thelist.length; } - // Using this function, you get the address and "value" at a given position in thelist. - function atPosition( uint256 idx ) public view returns (address,uint256) { + // Using this function, you get the address and topuplimit at a given position in thelist. + function atPosition( uint idx ) public view returns ( address, uint ) { require( idx <= thelist.length, "There are not that many addresses in the list." ); - return (thelist[idx].beneficiary,thelist[idx].value); + return (thelist[idx].beneficiary,thelist[idx].topuplimit); } - // Returns a (signed) position of where an address can be found in thelist. - // Or returns -1 if the address is not found in thelist. - function _beneficiaryPosition( address beneficiary ) internal view returns (int256) { - uint256 pos = thelist.length & 0x7fffffffffffffffffffffffffffffff; + // Returns a position +1 of where an address can be found in thelist. + // Or returns 0 if the address is not found in thelist. + // 0 : not found + // 1 : first position + function _beneficiaryPosition( address beneficiary ) internal view returns ( uint ) { + uint pos = thelist.length; while ( pos-- > 0 ) if ( beneficiary == thelist[pos].beneficiary ) - return int256( pos & 0x7fffffffffffffffffffffffffffffff ); - return -1; + return pos+1; + return 0; } // This function returns the "allowance" that a given address is set to. // Using this function, you don't have to cycle through atPosition() until // you find the address you want to know about. function getEtherAllowance( address beneficiary ) public view returns (uint256) { - int256 pos = _beneficiaryPosition( beneficiary ); - if ( pos == -1 ) + uint pos = _beneficiaryPosition( beneficiary ); + if ( pos == 0 ) return 0; - return thelist[uint256(pos)].value; + return thelist[pos-1].topuplimit; } // This admin (ownerOnly) function allows the creator of the contract to // add/change/delete "allowances" per address. - function setEtherAllowance( address beneficiary, uint256 value ) public onlyOwner { - int256 pos = _beneficiaryPosition( beneficiary ); - if ( value > 0 ) { - if ( pos == -1 ) - // Add the address to thelist if it was not already there - thelist.push( Allowances(beneficiary, value) ); - else - // Simple update the value of this address - thelist[uint256(pos)].value = value; + function setEtherAllowance( address beneficiary, uint256 topuplimit ) public onlyOwner { + uint pos = _beneficiaryPosition( beneficiary ); + // Not found and trying to delete beneficiary? Just return immediately. + if ( pos == 0 && topuplimit == 0 ) + return; + emit setAllowance( beneficiary, topuplimit ); + // not found + if ( pos == 0 ) + { + if ( topuplimit > 0 ) + // Add the address to thelist because it was not already there + thelist.push( Allowances(beneficiary,topuplimit) ); return; - } else { - // The beneficiary is set to have 0 Ether, so we - // delete the beneficiary from the list - uint i = uint256(pos) + 1; - while ( i++ < thelist.length ) - thelist[i] = thelist[i+1]; - // If distpos was past the position that we removed, - // then move that back one. - if ( distpos > uint256(pos) ) - distpos--; - // Shorten the list - thelist.length--; } + // Now use a properly zero-indexed pos + pos--; + // + if ( topuplimit > 0 ) { + // Simple update the topuplimit of this address + thelist[pos].topuplimit = topuplimit; + return; + } + // The beneficiary is set to have 0 Ether, so we + // delete the beneficiary from the list + uint i = pos; + while ( i++ < thelist.length ) + thelist[i-1] = thelist[i]; + // Shorten the list + thelist.length--; + // If distpos was past the position that we removed, + // then move that back one. + if ( distpos >= pos ) + distpos--; } function selfDestruct() public onlyOwner { selfdestruct( owner ); } + function mayDistribute() public view returns ( bool ) + { + return msg.sender == owner; + } function distribute() external { - uint256 listlength = thelist.length; + require( mayDistribute(), "You are not authorized to activate the distribution functionality." ); + emit distributeStartedBy( msg.sender ); // Is there anything to do at all + uint listlength = thelist.length; if ( listlength == 0 ) return; // Has the list gotten shorter since we we were last time? // This shouldn't happen, but it's better to be safe than to be sorry. if ( distpos >= listlength ) - distpos = 0; - uint256 wheretostop = distpos; + distpos = 0; + uint wheretostop = distpos; while ( gasleft() > 54321 ) { // Did we get to the end of the list, then start over if ( ++distpos >= listlength ) - distpos = 0; - uint256 balance = thelist[distpos].beneficiary.balance; - uint256 value = thelist[distpos].value; - if ( balance < value && (value+diff) > 0 ) + distpos = 0; + uint blockchainbalance = thelist[distpos].beneficiary.balance; + uint topuplimit = thelist[distpos].topuplimit; + uint diff = topuplimit - blockchainbalance; + // Don't top up anyone, if they still more than 90% of their allowance. + if ( blockchainbalance > topuplimit*9/10 ) + diff = 0; + if ( diff > 0 ) { - uint256 diff = value - balance; // we use send() instead of transfer(), because // transfer() can throw(), and we don't want // to stop processing because of a single error. + // - + // Use || true to avoid warnings from the compiler. + emit xfrAllowance( thelist[distpos].beneficiary, diff ); thelist[distpos].beneficiary.send( diff ) || true; } if ( wheretostop == distpos )