2021-02-02 11:26:44 +01:00
pragma solidity > 0 . 6 . 11 ;
// SPDX-License-Identifier: GPL-3.0-or-later
2021-02-05 08:45:44 +01:00
// TODO: assign bitmask values to contants
2021-02-02 11:26:44 +01:00
contract RedistributedDemurrageToken {
address public owner ;
string public name ;
string public symbol ;
2021-02-06 15:18:30 +01:00
uint256 public decimals ;
2021-02-02 11:26:44 +01:00
uint256 public totalSupply ;
2021-02-06 15:18:30 +01:00
uint256 public minimumParticipantSpend ;
uint256 constant ppmDivider = 100000000000000000000000000000000 ;
2021-02-02 11:26:44 +01:00
2021-02-06 15:18:30 +01:00
uint256 public immutable periodStart ; // timestamp
uint256 public immutable periodDuration ; // duration in SECONDS
uint256 public immutable taxLevel ; // PPM per MINUTE
uint256 public demurrageModifier ; // PPM uint128(block) | uint128(ppm)
2021-02-02 11:26:44 +01:00
2021-02-06 19:10:27 +01:00
//bytes32[] public redistributions; // uint1(isFractional) | uint1(unused) | uint38(participants) | uint160(value) | uint56(period)
bytes32 [ ] public redistributions ; // uint1(isFractional) | uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
//mapping (address => bytes32) account; // uint20(unused) | uint56(period) | uint160(value)
mapping ( address => bytes32 ) account ; // uint152(unused) | uint32(period) | uint72(value)
2021-02-02 16:10:21 +01:00
mapping ( address => bool ) minter ;
2021-02-05 09:22:36 +01:00
mapping ( address => mapping ( address => uint256 ) ) allowance ; // holder -> spender -> amount (amount is subject to demurrage)
2021-02-02 11:26:44 +01:00
2021-02-05 09:44:15 +01:00
address sinkAddress ; // receives redistribuion remainders
2021-02-02 11:26:44 +01:00
event Transfer ( address indexed _from , address indexed _to , uint256 _value ) ;
event Approval ( address indexed _owner , address indexed _spender , uint256 _value ) ;
2021-02-03 09:00:24 +01:00
event Mint ( address indexed _minter , address indexed _beneficiary , uint256 _value ) ;
2021-02-06 19:58:41 +01:00
event Debug ( bytes32 _foo ) ;
2021-02-06 15:18:30 +01:00
event Decayed ( uint256 indexed _period , uint256 indexed _periodCount , uint256 indexed _oldAmount , uint256 _newAmount ) ;
2021-02-03 09:00:24 +01:00
event Redistribution ( address indexed _account , uint256 indexed _period , uint256 _value ) ;
2021-02-02 11:26:44 +01:00
2021-02-06 15:18:30 +01:00
constructor ( string memory _name , string memory _symbol , uint8 _decimals , uint256 _taxLevelMinute , uint256 _periodMinutes , address _defaultSinkAddress ) public {
2021-02-02 11:26:44 +01:00
owner = msg . sender ;
2021-02-02 16:10:21 +01:00
minter [ owner ] = true ;
2021-02-06 15:18:30 +01:00
periodStart = block . timestamp ;
periodDuration = _periodMinutes * 60 ;
2021-02-02 11:26:44 +01:00
name = _name ;
symbol = _symbol ;
2021-02-05 09:22:36 +01:00
decimals = _decimals ;
2021-02-06 15:18:30 +01:00
demurrageModifier = ppmDivider * 1000000 ; // Emulates 38 decimal places
demurrageModifier |= ( 1 << 128 ) ;
taxLevel = _taxLevelMinute ; // 38 decimal places
2021-02-05 12:56:57 +01:00
sinkAddress = _defaultSinkAddress ;
2021-02-06 19:10:27 +01:00
bytes32 initialRedistribution = toRedistribution ( 0 , 1000000 , 0 , 1 ) ;
2021-02-02 11:26:44 +01:00
redistributions . push ( initialRedistribution ) ;
2021-02-06 15:18:30 +01:00
minimumParticipantSpend = 10 ** uint256 ( _decimals ) ;
2021-02-02 11:26:44 +01:00
}
2021-02-05 12:02:05 +01:00
// Given address will be allowed to call the mintTo() function
2021-02-02 16:10:21 +01:00
function addMinter ( address _minter ) public returns ( bool ) {
require ( msg . sender == owner ) ;
minter [ _minter ] = true ;
return true ;
}
2021-02-06 15:18:30 +01:00
2021-02-03 09:16:01 +01:00
/// ERC20
2021-02-02 16:10:21 +01:00
function balanceOf ( address _account ) public view returns ( uint256 ) {
2021-02-06 15:18:30 +01:00
uint256 baseBalance ;
uint256 anchorDemurrageAmount ;
uint256 anchorDemurragePeriod ;
uint256 currentDemurrageAmount ;
uint256 periodCount ;
baseBalance = getBaseBalance ( _account ) ;
anchorDemurrageAmount = toDemurrageAmount ( demurrageModifier ) ;
anchorDemurragePeriod = toDemurragePeriod ( demurrageModifier ) ;
periodCount = actualPeriod ( ) - toDemurragePeriod ( demurrageModifier ) ;
2021-02-06 19:58:41 +01:00
currentDemurrageAmount = decayBy ( anchorDemurrageAmount , periodCount ) ;
2021-02-06 15:18:30 +01:00
return ( baseBalance * currentDemurrageAmount ) / ( ppmDivider * 1000000 ) ;
2021-02-02 16:10:21 +01:00
}
2021-02-03 09:16:01 +01:00
/// Balance unmodified by demurrage
2021-02-02 16:10:21 +01:00
function getBaseBalance ( address _account ) private view returns ( uint256 ) {
2021-02-06 19:10:27 +01:00
//return uint256(account[_account]) & 0x00ffffffffffffffffffffffffffffffffffffffff;
return uint256 ( account [ _account ] ) & 0xffffffffffffffffff ;
2021-02-02 16:10:21 +01:00
}
2021-02-03 09:16:01 +01:00
/// Increases base balance for a single account
2021-02-05 12:56:57 +01:00
function increaseBaseBalance ( address _account , uint256 _delta ) private returns ( bool ) {
2021-02-02 21:14:41 +01:00
uint256 oldBalance ;
uint256 newBalance ;
2021-02-06 19:10:27 +01:00
uint256 workAccount ;
workAccount = uint256 ( account [ _account ] ) ; // | (newBalance & 0xffffffffffffffffff);
2021-02-06 15:18:30 +01:00
if ( _delta == 0 ) {
return false ;
}
2021-02-02 21:14:41 +01:00
oldBalance = getBaseBalance ( _account ) ;
newBalance = oldBalance + _delta ;
2021-02-05 12:56:57 +01:00
require ( uint160 ( newBalance ) > uint160 ( oldBalance ) , ' ERR_WOULDWRAP ' ) ; // revert if increase would result in a wrapped value
2021-02-06 19:10:27 +01:00
//account[_account] &= bytes32(0xfffffffffffffffffffffff0000000000000000000000000000000000000000);
//account[_account] = bytes32(uint256(account[_account]) & 0xfffffffffffffffffffffffffffffffffffffffffffff000000000000000000);
workAccount &= 0xfffffffffffffffffffffffffffffffffffffffffffff000000000000000000 ;
//account[_account] |= bytes32(newBalance & 0x00ffffffffffffffffffffffffffffffffffffffff);
workAccount |= newBalance & 0xffffffffffffffffff ;
account [ _account ] = bytes32 ( workAccount ) ;
2021-02-02 16:10:21 +01:00
return true ;
}
2021-02-03 09:16:01 +01:00
/// Decreases base balance for a single account
2021-02-05 12:56:57 +01:00
function decreaseBaseBalance ( address _account , uint256 _delta ) private returns ( bool ) {
2021-02-02 21:14:41 +01:00
uint256 oldBalance ;
uint256 newBalance ;
2021-02-06 19:10:27 +01:00
uint256 workAccount ;
workAccount = uint256 ( account [ _account ] ) ; // | (newBalance & 0xffffffffffffffffff);
2021-02-02 21:14:41 +01:00
2021-02-06 15:18:30 +01:00
if ( _delta == 0 ) {
return false ;
}
2021-02-02 21:14:41 +01:00
oldBalance = getBaseBalance ( _account ) ;
2021-02-05 12:56:57 +01:00
require ( oldBalance >= _delta , ' ERR_OVERSPEND ' ) ; // overspend guard
2021-02-02 21:14:41 +01:00
newBalance = oldBalance - _delta ;
2021-02-06 19:10:27 +01:00
//account[_account] &= bytes32(0xffffffffffffffffffffffff0000000000000000000000000000000000000000);
workAccount &= 0xfffffffffffffffffffffffffffffffffffffffffffff000000000000000000 ;
//account[_account] |= bytes32(newBalance & 0x00ffffffffffffffffffffffffffffffffffffffff);
workAccount |= newBalance & 0xffffffffffffffffff ;
account [ _account ] = bytes32 ( workAccount ) ;
2021-02-02 16:10:21 +01:00
return true ;
}
2021-02-03 09:16:01 +01:00
// Creates new tokens out of thin air, and allocates them to the given address
// Triggers tax
2021-02-02 16:10:21 +01:00
function mintTo ( address _beneficiary , uint256 _amount ) external returns ( bool ) {
2021-02-06 15:48:40 +01:00
uint256 baseAmount ;
2021-02-02 16:10:21 +01:00
require ( minter [ msg . sender ] ) ;
2021-02-02 19:09:13 +01:00
2021-02-06 15:18:30 +01:00
changePeriod ( ) ;
2021-02-06 15:48:40 +01:00
baseAmount = _amount ;
2021-02-02 17:53:35 +01:00
totalSupply += _amount ;
2021-02-06 15:48:40 +01:00
increaseBaseBalance ( _beneficiary , baseAmount ) ;
2021-02-02 16:10:21 +01:00
emit Mint ( msg . sender , _beneficiary , _amount ) ;
2021-02-02 17:53:35 +01:00
saveRedistributionSupply ( ) ;
2021-02-02 16:10:21 +01:00
return true ;
}
2021-02-03 09:16:01 +01:00
// Deserializes the redistribution word
2021-02-06 19:10:27 +01:00
// uint1(isFractional) | uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
function toRedistribution ( uint256 _participants , uint256 _demurrageModifierPpm , uint256 _value , uint256 _period ) private pure returns ( bytes32 ) {
2021-02-02 11:26:44 +01:00
bytes32 redistribution ;
2021-02-02 17:53:35 +01:00
2021-02-06 19:10:27 +01:00
redistribution |= bytes32 ( ( _demurrageModifierPpm & 0x0fffff ) << 140 ) ;
redistribution |= bytes32 ( ( _participants & 0x0fffffffff ) << 104 ) ;
redistribution |= bytes32 ( ( _value & 0xffffffffffffffffff ) << 32 ) ;
redistribution |= bytes32 ( _period & 0xffffffff ) ;
2021-02-02 11:26:44 +01:00
return redistribution ;
}
2021-02-03 09:16:01 +01:00
// Serializes the demurrage period part of the redistribution word
2021-02-02 11:26:44 +01:00
function toRedistributionPeriod ( bytes32 redistribution ) public pure returns ( uint256 ) {
2021-02-06 19:10:27 +01:00
return uint256 ( redistribution ) & 0xffffffff ;
2021-02-02 11:26:44 +01:00
}
2021-02-03 09:16:01 +01:00
// Serializes the supply part of the redistribution word
2021-02-02 21:14:41 +01:00
function toRedistributionSupply ( bytes32 redistribution ) public pure returns ( uint256 ) {
2021-02-06 19:10:27 +01:00
return uint256 ( redistribution & 0x00000000000000000000000000000000000000ffffffffffffffffff00000000 ) >> 32 ;
2021-02-02 21:14:41 +01:00
}
2021-02-03 09:16:01 +01:00
// Serializes the number of participants part of the redistribution word
2021-02-02 21:14:41 +01:00
function toRedistributionParticipants ( bytes32 redistribution ) public pure returns ( uint256 ) {
2021-02-06 19:10:27 +01:00
return uint256 ( redistribution & 0x00000000000000000000000000000fffffffff00000000000000000000000000 ) >> 104 ;
}
// Serializes the number of participants part of the redistribution word
function toRedistributionDemurrageModifier ( bytes32 redistribution ) public pure returns ( uint256 ) {
return uint256 ( redistribution & 0x000000000000000000000000fffff00000000000000000000000000000000000 ) >> 140 ;
2021-02-02 21:14:41 +01:00
}
2021-02-03 09:16:01 +01:00
// Client accessor to the redistributions array length
2021-02-02 11:26:44 +01:00
function redistributionCount ( ) public view returns ( uint256 ) {
return redistributions . length ;
}
2021-02-03 09:16:01 +01:00
// Add number of participants for the current redistribution period by one
2021-02-02 17:53:35 +01:00
function incrementRedistributionParticipants ( ) private returns ( bool ) {
2021-02-06 19:10:27 +01:00
bytes32 currentRedistribution ;
uint256 tmpRedistribution ;
2021-02-02 17:53:35 +01:00
uint256 participants ;
2021-02-06 19:10:27 +01:00
currentRedistribution = redistributions [ redistributions . length - 1 ] ;
participants = toRedistributionParticipants ( currentRedistribution ) + 1 ;
tmpRedistribution = uint256 ( currentRedistribution ) ;
tmpRedistribution &= 0xfffffffffffffffffffffffffffff000000000ffffffffffffffffffffffffff ;
tmpRedistribution |= ( participants & 0x0fffffffff ) << 104 ;
2021-02-02 17:53:35 +01:00
2021-02-06 19:10:27 +01:00
redistributions [ redistributions . length - 1 ] = bytes32 ( tmpRedistribution ) ;
2021-02-06 19:58:41 +01:00
return true ;
2021-02-02 17:53:35 +01:00
}
2021-02-03 09:16:01 +01:00
// Save the current total supply amount to the current redistribution period
2021-02-02 17:53:35 +01:00
function saveRedistributionSupply ( ) private returns ( bool ) {
uint256 currentRedistribution ;
currentRedistribution = uint256 ( redistributions [ redistributions . length - 1 ] ) ;
2021-02-06 19:10:27 +01:00
currentRedistribution &= 0xffffffffffffffffffffffffffffffffffffff000000000000000000ffffffff ;
currentRedistribution |= totalSupply << 32 ;
2021-02-02 17:53:35 +01:00
redistributions [ redistributions . length - 1 ] = bytes32 ( currentRedistribution ) ;
2021-02-06 19:58:41 +01:00
return true ;
2021-02-02 17:53:35 +01:00
}
2021-02-03 09:16:01 +01:00
// Get the demurrage period of the current block number
2021-02-02 11:26:44 +01:00
function actualPeriod ( ) public view returns ( uint256 ) {
2021-02-06 15:18:30 +01:00
return ( block . timestamp - periodStart ) / periodDuration + 1 ;
2021-02-02 11:26:44 +01:00
}
2021-02-03 09:16:01 +01:00
// Add an entered demurrage period to the redistribution array
2021-02-02 11:26:44 +01:00
function checkPeriod ( ) private view returns ( bytes32 ) {
2021-02-02 21:14:41 +01:00
bytes32 lastRedistribution ;
uint256 currentPeriod ;
lastRedistribution = redistributions [ redistributions . length - 1 ] ;
currentPeriod = this . actualPeriod ( ) ;
2021-02-02 19:09:13 +01:00
if ( currentPeriod <= toRedistributionPeriod ( lastRedistribution ) ) {
2021-02-02 11:26:44 +01:00
return bytes32 ( 0x00 ) ;
}
return lastRedistribution ;
}
2021-02-03 09:16:01 +01:00
// Deserialize the pemurrage period for the given account is participating in
2021-02-05 12:56:57 +01:00
function accountPeriod ( address _account ) public view returns ( uint256 ) {
2021-02-06 19:10:27 +01:00
//return (uint256(account[_account]) & 0xffffffffffffffffffffffff0000000000000000000000000000000000000000) >> 160;
return ( uint256 ( account [ _account ] ) & 0x00000000000000000000000000000000000000ffffffff000000000000000000 ) >> 72 ;
2021-02-02 21:14:41 +01:00
}
2021-02-03 09:16:01 +01:00
// Save the given demurrage period as the currently participation period for the given address
2021-02-02 21:14:41 +01:00
function registerAccountPeriod ( address _account , uint256 _period ) private returns ( bool ) {
2021-02-06 19:10:27 +01:00
//account[_account] &= 0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff;
account [ _account ] &= 0xffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffff ;
account [ _account ] |= bytes32 ( _period << 72 ) ;
2021-02-02 21:14:41 +01:00
incrementRedistributionParticipants ( ) ;
2021-02-06 19:58:41 +01:00
return true ;
2021-02-02 21:14:41 +01:00
}
2021-02-05 12:02:05 +01:00
// Determine whether the unit number is rounded down, rounded up or evenly divides.
// Returns 0 if evenly distributed, or the remainder as a positive number
2021-02-05 12:56:57 +01:00
// A _numParts value 0 will be interpreted as the value 1
2021-02-05 12:02:05 +01:00
function remainder ( uint256 _numParts , uint256 _sumWhole ) public pure returns ( uint256 ) {
uint256 unit ;
uint256 truncatedResult ;
2021-02-05 12:56:57 +01:00
if ( _numParts == 0 ) { // no division by zero please
2021-02-06 15:18:30 +01:00
revert ( ' ERR_NUMPARTS_ZERO ' ) ;
2021-02-05 12:02:05 +01:00
}
2021-02-05 12:56:57 +01:00
require ( _numParts < _sumWhole ) ; // At least you are never LESS than the sum of your parts. Think about that.
2021-02-05 12:02:05 +01:00
unit = _sumWhole / _numParts ;
truncatedResult = unit * _numParts ;
return _sumWhole - truncatedResult ;
}
2021-02-05 12:32:17 +01:00
2021-02-06 15:18:30 +01:00
// Called in the edge case where participant number is 0. It will override the participant count to 1.
// Returns the remainder sent to the sink address
function applyDefaultRedistribution ( bytes32 _redistribution ) private returns ( uint256 ) {
uint256 redistributionSupply ;
uint256 redistributionPeriod ;
uint256 unit ;
uint256 truncatedResult ;
redistributionSupply = toRedistributionSupply ( _redistribution ) ;
unit = ( redistributionSupply * taxLevel ) / 1000000 ;
truncatedResult = ( unit * 1000000 ) / taxLevel ;
if ( truncatedResult < redistributionSupply ) {
redistributionPeriod = toRedistributionPeriod ( _redistribution ) ; // since we reuse period here, can possibly be optimized by passing period instead
2021-02-06 19:10:27 +01:00
redistributions [ redistributionPeriod - 1 ] &= 0xfffffffffffffffffffffffffffff000000000ffffffffffffffffffffffffff ; // just to be safe, zero out all participant count data, in this case there will be only one
redistributions [ redistributionPeriod - 1 ] |= 0x8000000000000000000000000000000000000100000000000000000000000000 ;
2021-02-06 15:18:30 +01:00
}
increaseBaseBalance ( sinkAddress , unit / ppmDivider ) ; //truncatedResult);
return unit ;
}
2021-02-05 12:32:17 +01:00
// sets the remainder bit for the given period and books the remainder to the sink address balance
// returns false if no change was made
function applyRemainderOnPeriod ( uint256 _remainder , uint256 _period ) private returns ( bool ) {
2021-02-06 15:18:30 +01:00
uint256 periodSupply ;
2021-02-05 12:32:17 +01:00
if ( _remainder == 0 ) {
return false ;
}
2021-02-06 15:18:30 +01:00
2021-02-06 19:10:27 +01:00
// is this needed?
2021-02-06 15:18:30 +01:00
redistributions [ _period - 1 ] |= 0x8000000000000000000000000000000000000000000000000000000000000000 ;
periodSupply = toRedistributionSupply ( redistributions [ _period - 1 ] ) ;
increaseBaseBalance ( sinkAddress , periodSupply - _remainder ) ;
return true ;
}
function toDemurrageAmount ( uint256 _demurrage ) public pure returns ( uint256 ) {
return _demurrage & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff ;
}
function toDemurragePeriod ( uint256 _demurrage ) public pure returns ( uint256 ) {
return ( _demurrage & 0xffffffffffffffffffffffffffffffff00000000000000000000000000000000 ) >> 128 ;
}
function applyDemurrage ( ) public returns ( bool ) {
uint256 epochPeriodCount ;
uint256 periodCount ;
uint256 lastDemurrageAmount ;
uint256 newDemurrageAmount ;
epochPeriodCount = actualPeriod ( ) ;
//epochPeriodCount = (block.timestamp - periodStart) / periodDuration; // toDemurrageTime(demurrageModifier);
periodCount = epochPeriodCount - toDemurragePeriod ( demurrageModifier ) ;
if ( periodCount == 0 ) {
return false ;
}
lastDemurrageAmount = toDemurrageAmount ( demurrageModifier ) ;
2021-02-06 19:58:41 +01:00
newDemurrageAmount = decayBy ( lastDemurrageAmount , periodCount ) ;
2021-02-06 15:18:30 +01:00
demurrageModifier = 0 ;
demurrageModifier |= ( newDemurrageAmount & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff ) ;
demurrageModifier |= ( epochPeriodCount << 128 ) ;
emit Decayed ( epochPeriodCount , periodCount , lastDemurrageAmount , newDemurrageAmount ) ;
2021-02-05 12:02:05 +01:00
return true ;
}
2021-02-06 19:10:27 +01:00
// Return timestamp of start of period threshold
function getPeriodTimeDelta ( uint256 _periodCount ) public view returns ( uint256 ) {
return periodStart + ( _periodCount * periodDuration ) ;
}
// Amount of demurrage cycles inbetween the current timestamp and the given target time
function demurrageCycles ( uint256 _target ) public view returns ( uint256 ) {
return ( block . timestamp - _target ) / 60 ;
}
2021-02-03 09:16:01 +01:00
// Recalculate the demurrage modifier for the new period
2021-02-05 12:02:05 +01:00
// After this, all REPORTED balances will have been reduced by the corresponding ratio (but the effecive totalsupply stays the same)
2021-02-06 15:18:30 +01:00
//function applyTax() public returns (uint256) {
2021-02-06 19:10:27 +01:00
function changePeriod ( ) public returns ( bool ) {
2021-02-05 12:02:05 +01:00
bytes32 currentRedistribution ;
2021-02-02 11:26:44 +01:00
bytes32 nextRedistribution ;
2021-02-02 16:10:21 +01:00
uint256 currentPeriod ;
2021-02-05 12:02:05 +01:00
uint256 currentParticipants ;
uint256 currentRemainder ;
2021-02-06 19:58:41 +01:00
uint256 currentDemurrageAmount ;
uint256 nextRedistributionDemurrage ;
2021-02-06 19:10:27 +01:00
uint256 demurrageCounts ;
uint256 periodTimestamp ;
2021-02-02 11:26:44 +01:00
2021-02-05 12:02:05 +01:00
currentRedistribution = checkPeriod ( ) ;
if ( currentRedistribution == bytes32 ( 0x00 ) ) {
2021-02-06 19:10:27 +01:00
return false ;
2021-02-02 11:26:44 +01:00
}
2021-02-06 19:58:41 +01:00
currentPeriod = toRedistributionPeriod ( currentRedistribution ) ;
2021-02-06 19:10:27 +01:00
periodTimestamp = getPeriodTimeDelta ( currentPeriod ) ;
2021-02-06 19:58:41 +01:00
applyDemurrage ( ) ;
currentDemurrageAmount = toDemurrageAmount ( demurrageModifier ) ;
2021-02-06 19:10:27 +01:00
demurrageCounts = demurrageCycles ( periodTimestamp ) ;
2021-02-06 19:58:41 +01:00
if ( demurrageCounts > 0 ) {
nextRedistributionDemurrage = growBy ( currentDemurrageAmount , demurrageCounts ) / ppmDivider ;
} else {
nextRedistributionDemurrage = currentDemurrageAmount / ppmDivider ;
}
2021-02-06 19:10:27 +01:00
2021-02-06 19:58:41 +01:00
nextRedistribution = toRedistribution ( 0 , nextRedistributionDemurrage , totalSupply , currentPeriod + 1 ) ;
emit Debug ( bytes32 ( currentDemurrageAmount ) ) ;
2021-02-02 11:26:44 +01:00
redistributions . push ( nextRedistribution ) ;
2021-02-05 12:02:05 +01:00
currentParticipants = toRedistributionParticipants ( currentRedistribution ) ;
2021-02-06 15:18:30 +01:00
if ( currentParticipants == 0 ) {
currentRemainder = applyDefaultRedistribution ( currentRedistribution ) ;
} else {
currentRemainder = remainder ( currentParticipants , totalSupply ) ; // we can use totalSupply directly because it will always be the same as the recorded supply on the current redistribution
applyRemainderOnPeriod ( currentRemainder , currentPeriod ) ;
}
2021-02-06 19:10:27 +01:00
return true ;
2021-02-02 11:26:44 +01:00
}
2021-02-06 19:58:41 +01:00
function growBy ( uint256 _value , uint256 _period ) public view returns ( uint256 ) {
uint256 valueFactor ;
uint256 truncatedTaxLevel ;
// TODO: if can't get to work, reverse the iteration from current period.
valueFactor = 1000000 ;
truncatedTaxLevel = taxLevel / ppmDivider ;
for ( uint256 i = 0 ; i < _period ; i ++ ) {
valueFactor = valueFactor + ( ( valueFactor * truncatedTaxLevel ) / 1000000 ) ;
}
return ( valueFactor * _value ) / 1000000 ;
}
2021-02-03 09:16:01 +01:00
// Calculate a value reduced by demurrage by the given period
2021-02-06 19:10:27 +01:00
// TODO: higher precision
2021-02-06 19:58:41 +01:00
function decayBy ( uint256 _value , uint256 _period ) public view returns ( uint256 ) {
2021-02-03 09:00:24 +01:00
uint256 valueFactor ;
2021-02-06 15:18:30 +01:00
uint256 truncatedTaxLevel ;
2021-02-03 09:00:24 +01:00
2021-02-06 15:18:30 +01:00
// TODO: if can't get to work, reverse the iteration from current period.
2021-02-03 09:00:24 +01:00
valueFactor = 1000000 ;
2021-02-06 15:18:30 +01:00
truncatedTaxLevel = taxLevel / ppmDivider ;
2021-02-03 09:00:24 +01:00
for ( uint256 i = 0 ; i < _period ; i ++ ) {
2021-02-06 15:18:30 +01:00
valueFactor = valueFactor - ( ( valueFactor * truncatedTaxLevel ) / 1000000 ) ;
2021-02-03 09:00:24 +01:00
}
return ( valueFactor * _value ) / 1000000 ;
2021-02-02 16:46:27 +01:00
}
2021-02-03 09:16:01 +01:00
// If the given account is participating in a period and that period has been crossed
// THEN increase the base value of the account with its share of the value reduction of the period
2021-02-02 21:14:41 +01:00
function applyRedistributionOnAccount ( address _account ) public returns ( bool ) {
bytes32 periodRedistribution ;
uint256 supply ;
uint256 participants ;
2021-02-03 09:00:24 +01:00
uint256 baseValue ;
2021-02-02 21:14:41 +01:00
uint256 value ;
uint256 period ;
period = accountPeriod ( _account ) ;
2021-02-03 09:00:24 +01:00
if ( period == 0 || period >= actualPeriod ( ) ) {
2021-02-02 21:14:41 +01:00
return false ;
}
2021-02-03 09:00:24 +01:00
periodRedistribution = redistributions [ period - 1 ] ;
2021-02-02 21:14:41 +01:00
participants = toRedistributionParticipants ( periodRedistribution ) ;
if ( participants == 0 ) {
2021-02-05 12:56:57 +01:00
return false ;
2021-02-02 21:14:41 +01:00
}
2021-02-05 12:56:57 +01:00
2021-02-02 21:14:41 +01:00
supply = toRedistributionSupply ( periodRedistribution ) ;
2021-02-06 15:18:30 +01:00
baseValue = ( ( supply / participants ) * ( taxLevel / 1000000 ) ) / ppmDivider ;
2021-02-06 19:58:41 +01:00
value = decayBy ( baseValue , period - 1 ) ;
2021-02-02 21:14:41 +01:00
2021-02-06 19:10:27 +01:00
//account[_account] &= bytes32(0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff);
account [ _account ] &= bytes32 ( 0xffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffff ) ;
2021-02-05 12:56:57 +01:00
increaseBaseBalance ( _account , value ) ;
2021-02-02 21:14:41 +01:00
emit Redistribution ( _account , period , value ) ;
return true ;
2021-02-02 16:46:27 +01:00
}
2021-02-05 09:22:36 +01:00
// Inflates the given amount according to the current demurrage modifier
function toBaseAmount ( uint256 _value ) public view returns ( uint256 ) {
2021-02-06 15:18:30 +01:00
return ( _value * ppmDivider * 1000000 ) / toDemurrageAmount ( demurrageModifier ) ;
}
// ERC20, triggers tax and/or redistribution
function approve ( address _spender , uint256 _value ) public returns ( bool ) {
uint256 baseValue ;
changePeriod ( ) ;
applyRedistributionOnAccount ( msg . sender ) ;
baseValue = toBaseAmount ( _value ) ;
allowance [ msg . sender ] [ _spender ] += baseValue ;
emit Approval ( msg . sender , _spender , _value ) ;
return true ;
2021-02-05 09:22:36 +01:00
}
2021-02-03 09:16:01 +01:00
// ERC20, triggers tax and/or redistribution
2021-02-02 16:46:27 +01:00
function transfer ( address _to , uint256 _value ) public returns ( bool ) {
uint256 baseValue ;
bool result ;
2021-02-06 15:18:30 +01:00
changePeriod ( ) ;
2021-02-03 09:00:24 +01:00
applyRedistributionOnAccount ( msg . sender ) ;
2021-02-02 21:14:41 +01:00
// TODO: Prefer to truncate the result, instead it seems to round to nearest :/
2021-02-05 09:22:36 +01:00
baseValue = toBaseAmount ( _value ) ;
2021-02-02 16:46:27 +01:00
result = transferBase ( msg . sender , _to , baseValue ) ;
2021-02-02 21:14:41 +01:00
2021-02-02 16:10:21 +01:00
return result ;
}
2021-02-05 09:22:36 +01:00
// ERC20, triggers tax and/or redistribution
function transferFrom ( address _from , address _to , uint256 _value ) public returns ( bool ) {
uint256 baseValue ;
bool result ;
2021-02-06 15:18:30 +01:00
changePeriod ( ) ;
2021-02-05 09:22:36 +01:00
applyRedistributionOnAccount ( msg . sender ) ;
baseValue = toBaseAmount ( _value ) ;
require ( allowance [ _from ] [ msg . sender ] >= baseValue ) ;
result = transferBase ( _from , _to , baseValue ) ;
return result ;
}
2021-02-06 15:18:30 +01:00
// ERC20 transfer backend for transfer, transferFrom
function transferBase ( address _from , address _to , uint256 _value ) private returns ( bool ) {
uint256 period ;
2021-02-05 09:22:36 +01:00
2021-02-06 15:18:30 +01:00
decreaseBaseBalance ( _from , _value ) ;
increaseBaseBalance ( _to , _value ) ;
2021-02-05 09:22:36 +01:00
2021-02-06 15:18:30 +01:00
period = actualPeriod ( ) ;
if ( _value >= minimumParticipantSpend && accountPeriod ( _from ) != period && _from != _to ) {
registerAccountPeriod ( _from , period ) ;
}
2021-02-05 09:22:36 +01:00
return true ;
}
2021-02-02 11:26:44 +01:00
}