2021-02-02 11:26:44 +01:00
pragma solidity > 0 . 6 . 11 ;
// SPDX-License-Identifier: GPL-3.0-or-later
contract RedistributedDemurrageToken {
2021-03-01 10:23:55 +01:00
// Redistribution bit field, with associated shifts and masks
// (Uses sub-byte boundaries)
bytes32 [ ] public redistributions ; // uint1(isFractional) | uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
uint8 constant shiftRedistributionPeriod = 0 ;
uint256 constant maskRedistributionPeriod = 0x00000000000000000000000000000000000000000000000000000000ffffffff ; // (1 << 32) - 1
uint8 constant shiftRedistributionValue = 32 ;
uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000 ; // ((1 << 72) - 1) << 32
uint8 constant shiftRedistributionParticipants = 104 ;
uint256 constant maskRedistributionParticipants = 0x00000000000000000000000000000fffffffff00000000000000000000000000 ; // ((1 << 36) - 1) << 104
uint8 constant shiftRedistributionDemurrage = 140 ;
uint256 constant maskRedistributionDemurrage = 0x000000000000000000000000fffff00000000000000000000000000000000000 ; // ((1 << 20) - 1) << 140
uint8 constant shiftRedistributionIsFractional = 255 ;
uint256 constant maskRedistributionIsFractional = 0x8000000000000000000000000000000000000000000000000000000000000000 ; // 1 << 255
// Account bit field, with associated shifts and masks
// Mirrors structure of redistributions for consistency
mapping ( address => bytes32 ) account ; // uint152(unused) | uint32(period) | uint72(value)
uint8 constant shiftAccountValue = 0 ;
uint256 constant maskAccountValue = 0x0000000000000000000000000000000000000000000000ffffffffffffffffff ; // (1 << 72) - 1
uint8 constant shiftAccountPeriod = 72 ;
uint256 constant maskAccountPeriod = 0x00000000000000000000000000000000000000ffffffff000000000000000000 ; // ((1 << 32) - 1) << 72
2021-03-01 10:53:02 +01:00
// Cached demurrage amount, ppm with 38 digit resolution
uint128 public demurrageAmount ;
2021-03-01 10:23:55 +01:00
2021-03-01 10:53:02 +01:00
// Cached demurrage period; the period for which demurrageAmount was calculated
uint128 public demurragePeriod ;
2021-03-01 10:23:55 +01:00
// Implements EIP172
2021-02-02 11:26:44 +01:00
address public owner ;
2021-03-01 10:23:55 +01:00
2021-05-01 08:40:27 +02:00
address newOwner ;
2021-03-01 10:23:55 +01:00
// Implements ERC20
2021-02-02 11:26:44 +01:00
string public name ;
2021-03-01 10:23:55 +01:00
// Implements ERC20
2021-02-02 11:26:44 +01:00
string public symbol ;
2021-03-01 10:23:55 +01:00
// Implements ERC20
2021-02-06 15:18:30 +01:00
uint256 public decimals ;
2021-03-01 10:23:55 +01:00
// Implements ERC20
2021-02-02 11:26:44 +01:00
uint256 public totalSupply ;
2021-03-01 10:23:55 +01:00
// Minimum amount of (demurraged) tokens an account must spend to participate in redistribution for a particular period
2021-02-06 15:18:30 +01:00
uint256 public minimumParticipantSpend ;
2021-03-01 10:23:55 +01:00
// 128 bit resolution of the demurrage divisor
// (this constant x 1000000 is contained within 128 bits)
2021-02-06 15:18:30 +01:00
uint256 constant ppmDivider = 100000000000000000000000000000000 ;
2021-02-02 11:26:44 +01:00
2021-03-01 10:23:55 +01:00
// Timestamp of start of periods (time which contract constructor was called)
uint256 public immutable periodStart ;
2021-02-02 11:26:44 +01:00
2021-03-01 10:23:55 +01:00
// Duration of a single redistribution period in seconds
uint256 public immutable periodDuration ;
// Demurrage in ppm per minute
uint256 public immutable taxLevel ;
// Addresses allowed to mint new tokens
2021-02-02 16:10:21 +01:00
mapping ( address => bool ) minter ;
2021-03-01 10:23:55 +01:00
// Storage for ERC20 approve/transferFrom methods
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-03-01 10:23:55 +01:00
// Address to send unallocated redistribution tokens
address sinkAddress ;
2021-02-05 09:44:15 +01:00
2021-03-01 10:23:55 +01:00
// Implements ERC20
2021-02-02 11:26:44 +01:00
event Transfer ( address indexed _from , address indexed _to , uint256 _value ) ;
2021-03-01 10:23:55 +01:00
// Implements ERC20
2021-02-02 11:26:44 +01:00
event Approval ( address indexed _owner , address indexed _spender , uint256 _value ) ;
2021-03-01 10:23:55 +01:00
// New tokens minted
2021-02-03 09:00:24 +01:00
event Mint ( address indexed _minter , address indexed _beneficiary , uint256 _value ) ;
2021-03-01 10:23:55 +01:00
// New demurrage cache milestone calculated
2021-02-06 15:18:30 +01:00
event Decayed ( uint256 indexed _period , uint256 indexed _periodCount , uint256 indexed _oldAmount , uint256 _newAmount ) ;
2021-03-01 10:23:55 +01:00
// When a new period threshold has been crossed
2021-02-06 22:04:39 +01:00
event Period ( uint256 _period ) ;
2021-03-01 10:23:55 +01:00
// Redistribution applied on a single eligible account
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-03-01 10:23:55 +01:00
// Temporary event used in development, will be removed on prod
event Debug ( bytes32 _foo ) ;
2021-05-01 08:40:27 +02:00
// EIP173
event OwnershipTransferred ( address indexed previousOwner , address indexed newOwner ) ; // EIP173
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-03-01 10:23:55 +01:00
// ACL setup
2021-02-02 11:26:44 +01:00
owner = msg . sender ;
2021-02-02 16:10:21 +01:00
minter [ owner ] = true ;
2021-03-01 10:23:55 +01:00
// ERC20 setup
2021-02-02 11:26:44 +01:00
name = _name ;
symbol = _symbol ;
2021-02-05 09:22:36 +01:00
decimals = _decimals ;
2021-03-01 10:23:55 +01:00
// Demurrage setup
periodStart = block . timestamp ;
periodDuration = _periodMinutes * 60 ;
2021-03-01 10:53:02 +01:00
demurrageAmount = uint128 ( ppmDivider * 1000000 ) ; // Represents 38 decimal places
demurragePeriod = 1 ;
2021-02-06 20:14:42 +01:00
taxLevel = _taxLevelMinute ; // Represents 38 decimal places
2021-02-06 20:01:33 +01:00
bytes32 initialRedistribution = toRedistribution ( 0 , 1000000 , 0 , 1 ) ;
2021-02-02 11:26:44 +01:00
redistributions . push ( initialRedistribution ) ;
2021-03-01 10:23:55 +01:00
// Misc settings
sinkAddress = _defaultSinkAddress ;
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-03-01 10:23:55 +01:00
// Given address will no longer be allowed to call the mintTo() function
function removeMinter ( address _minter ) public returns ( bool ) {
require ( msg . sender == owner || _minter == msg . sender ) ;
minter [ _minter ] = false ;
return true ;
}
/// Implements 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 ;
2021-03-01 10:53:02 +01:00
uint256 currentDemurragedAmount ;
2021-02-06 15:18:30 +01:00
uint256 periodCount ;
2021-02-15 18:20:00 +01:00
baseBalance = baseBalanceOf ( _account ) ;
2021-02-06 15:18:30 +01:00
2021-03-01 10:53:02 +01:00
periodCount = actualPeriod ( ) - demurragePeriod ;
2021-02-06 15:18:30 +01:00
2021-03-01 10:53:02 +01:00
currentDemurragedAmount = uint128 ( decayBy ( demurrageAmount , periodCount ) ) ;
2021-02-06 15:18:30 +01:00
2021-03-01 10:53:02 +01:00
return ( baseBalance * currentDemurragedAmount ) / ( ppmDivider * 1000000 ) ;
2021-02-02 16:10:21 +01:00
}
2021-02-03 09:16:01 +01:00
/// Balance unmodified by demurrage
2021-02-15 18:20:00 +01:00
function baseBalanceOf ( address _account ) public view returns ( uint256 ) {
2021-03-01 10:23:55 +01:00
return uint256 ( account [ _account ] ) & maskAccountValue ;
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 20:01:33 +01:00
uint256 workAccount ;
2021-02-06 20:14:42 +01:00
workAccount = uint256 ( account [ _account ] ) ;
2021-02-06 15:18:30 +01:00
if ( _delta == 0 ) {
return false ;
}
2021-02-15 18:20:00 +01:00
oldBalance = baseBalanceOf ( _account ) ;
2021-02-02 21:14:41 +01:00
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-03-01 10:23:55 +01:00
workAccount &= ( ~ maskAccountValue ) ;
workAccount |= ( newBalance & maskAccountValue ) ;
2021-02-06 20:01:33 +01:00
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 20:01:33 +01:00
uint256 workAccount ;
2021-02-06 20:14:42 +01:00
workAccount = uint256 ( account [ _account ] ) ;
2021-02-02 21:14:41 +01:00
2021-02-06 15:18:30 +01:00
if ( _delta == 0 ) {
return false ;
}
2021-02-15 18:20:00 +01:00
oldBalance = baseBalanceOf ( _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-03-01 10:23:55 +01:00
workAccount &= ( ~ maskAccountValue ) ;
workAccount |= ( newBalance & maskAccountValue ) ;
2021-02-06 20:01:33 +01:00
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 20:01:33 +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-03-01 10:23:55 +01:00
redistribution |= bytes32 ( ( _demurrageModifierPpm << shiftRedistributionDemurrage ) & maskRedistributionDemurrage ) ;
redistribution |= bytes32 ( ( _participants << shiftRedistributionParticipants ) & maskRedistributionParticipants ) ;
redistribution |= bytes32 ( ( _value << shiftRedistributionValue ) & maskRedistributionValue ) ;
redistribution |= bytes32 ( _period & maskRedistributionPeriod ) ;
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-03-01 10:23:55 +01:00
return uint256 ( redistribution ) & maskRedistributionPeriod ;
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-03-01 10:23:55 +01:00
return ( uint256 ( redistribution ) & maskRedistributionValue ) >> shiftRedistributionValue ;
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-03-01 10:23:55 +01:00
return ( uint256 ( redistribution ) & maskRedistributionParticipants ) >> shiftRedistributionParticipants ;
2021-02-06 20:01:33 +01:00
}
// Serializes the number of participants part of the redistribution word
function toRedistributionDemurrageModifier ( bytes32 redistribution ) public pure returns ( uint256 ) {
2021-03-01 10:23:55 +01:00
return ( uint256 ( redistribution ) & maskRedistributionDemurrage ) >> shiftRedistributionDemurrage ;
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 20:01:33 +01:00
bytes32 currentRedistribution ;
uint256 tmpRedistribution ;
2021-02-02 17:53:35 +01:00
uint256 participants ;
2021-02-06 20:01:33 +01:00
currentRedistribution = redistributions [ redistributions . length - 1 ] ;
participants = toRedistributionParticipants ( currentRedistribution ) + 1 ;
tmpRedistribution = uint256 ( currentRedistribution ) ;
2021-03-01 10:23:55 +01:00
tmpRedistribution &= ( ~ maskRedistributionParticipants ) ;
tmpRedistribution |= ( ( participants << shiftRedistributionParticipants ) & maskRedistributionParticipants ) ;
2021-02-02 17:53:35 +01:00
2021-02-06 20:01:33 +01:00
redistributions [ redistributions . length - 1 ] = bytes32 ( tmpRedistribution ) ;
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-03-01 10:23:55 +01:00
currentRedistribution &= ( ~ maskRedistributionValue ) ;
currentRedistribution |= ( totalSupply << shiftRedistributionValue ) ;
2021-02-02 17:53:35 +01:00
redistributions [ redistributions . length - 1 ] = bytes32 ( currentRedistribution ) ;
2021-02-06 20:01:33 +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-03-01 10:53:02 +01:00
function actualPeriod ( ) public view returns ( uint128 ) {
return uint128 ( ( 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-03-01 10:23:55 +01:00
return ( uint256 ( account [ _account ] ) & maskAccountPeriod ) >> shiftAccountPeriod ;
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-03-01 10:23:55 +01:00
account [ _account ] &= bytes32 ( ~ maskAccountPeriod ) ;
account [ _account ] |= bytes32 ( ( _period << shiftAccountPeriod ) & maskAccountPeriod ) ;
2021-02-02 21:14:41 +01:00
incrementRedistributionParticipants ( ) ;
2021-02-06 20:01:33 +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-03-01 10:23:55 +01:00
redistributions [ redistributionPeriod - 1 ] &= bytes32 ( ~ maskRedistributionParticipants ) ; // just to be safe, zero out all participant count data, in this case there will be only one
redistributions [ redistributionPeriod - 1 ] |= bytes32 ( maskRedistributionIsFractional | ( 1 << shiftRedistributionParticipants ) ) ;
2021-02-06 15:18:30 +01:00
}
2021-03-01 10:23:55 +01:00
increaseBaseBalance ( sinkAddress , unit / ppmDivider ) ;
2021-02-06 15:18:30 +01:00
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-03-01 10:23:55 +01:00
// TODO: is this needed?
redistributions [ _period - 1 ] |= bytes32 ( maskRedistributionIsFractional ) ;
2021-02-06 15:18:30 +01:00
periodSupply = toRedistributionSupply ( redistributions [ _period - 1 ] ) ;
increaseBaseBalance ( sinkAddress , periodSupply - _remainder ) ;
return true ;
}
2021-03-01 10:23:55 +01:00
// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
2021-02-06 15:18:30 +01:00
function applyDemurrage ( ) public returns ( bool ) {
2021-03-01 10:53:02 +01:00
uint128 epochPeriodCount ;
uint128 periodCount ;
2021-02-06 15:18:30 +01:00
uint256 lastDemurrageAmount ;
uint256 newDemurrageAmount ;
epochPeriodCount = actualPeriod ( ) ;
2021-03-01 10:53:02 +01:00
periodCount = epochPeriodCount - demurragePeriod ;
2021-02-06 15:18:30 +01:00
if ( periodCount == 0 ) {
return false ;
}
2021-03-01 10:53:02 +01:00
lastDemurrageAmount = demurrageAmount ;
demurrageAmount = uint128 ( decayBy ( lastDemurrageAmount , periodCount ) ) ;
demurragePeriod = epochPeriodCount ;
emit Decayed ( epochPeriodCount , periodCount , lastDemurrageAmount , demurrageAmount ) ;
2021-02-05 12:02:05 +01:00
return true ;
}
2021-02-06 20:01:33 +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 20:01:33 +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 20:01:33 +01:00
uint256 currentDemurrageAmount ;
uint256 nextRedistributionDemurrage ;
uint256 demurrageCounts ;
uint256 periodTimestamp ;
2021-02-06 22:04:39 +01:00
uint256 nextPeriod ;
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 20:01:33 +01:00
return false ;
2021-02-02 11:26:44 +01:00
}
2021-02-06 20:01:33 +01:00
2021-02-05 12:02:05 +01:00
currentPeriod = toRedistributionPeriod ( currentRedistribution ) ;
2021-02-06 22:04:39 +01:00
nextPeriod = currentPeriod + 1 ;
2021-02-06 20:01:33 +01:00
periodTimestamp = getPeriodTimeDelta ( currentPeriod ) ;
applyDemurrage ( ) ;
2021-03-01 10:53:02 +01:00
currentDemurrageAmount = demurrageAmount ;
2021-02-06 20:01:33 +01:00
demurrageCounts = demurrageCycles ( periodTimestamp ) ;
if ( demurrageCounts > 0 ) {
nextRedistributionDemurrage = growBy ( currentDemurrageAmount , demurrageCounts ) / ppmDivider ;
} else {
nextRedistributionDemurrage = currentDemurrageAmount / ppmDivider ;
}
2021-02-06 22:04:39 +01:00
nextRedistribution = toRedistribution ( 0 , nextRedistributionDemurrage , totalSupply , nextPeriod ) ;
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 22:04:39 +01:00
emit Period ( nextPeriod ) ;
2021-02-06 20:01:33 +01:00
return true ;
}
2021-03-01 10:23:55 +01:00
// Reverse a value reduced by demurrage by the given period to its original value
2021-02-06 20:01:33 +01:00
function growBy ( uint256 _value , uint256 _period ) public view returns ( uint256 ) {
uint256 valueFactor ;
uint256 truncatedTaxLevel ;
valueFactor = 1000000 ;
truncatedTaxLevel = taxLevel / ppmDivider ;
for ( uint256 i = 0 ; i < _period ; i ++ ) {
valueFactor = valueFactor + ( ( valueFactor * truncatedTaxLevel ) / 1000000 ) ;
}
return ( valueFactor * _value ) / 1000000 ;
2021-02-02 11:26:44 +01:00
}
2021-02-03 09:16:01 +01:00
// Calculate a value reduced by demurrage by the given period
2021-03-01 10:23:55 +01:00
// TODO: higher precision if possible
2021-02-06 20:01:33 +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
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 ;
2021-02-06 22:04:39 +01:00
uint256 demurrage ;
2021-02-02 21:14:41 +01:00
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 22:04:39 +01:00
demurrage = toRedistributionDemurrageModifier ( periodRedistribution ) ;
2021-02-06 15:18:30 +01:00
baseValue = ( ( supply / participants ) * ( taxLevel / 1000000 ) ) / ppmDivider ;
2021-02-06 22:04:39 +01:00
value = ( baseValue * demurrage ) / 1000000 ;
2021-02-02 21:14:41 +01:00
2021-03-01 10:23:55 +01:00
// zero out period for the account
account [ _account ] &= bytes32 ( ~ maskAccountPeriod ) ;
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-03-01 10:53:02 +01:00
//return (_value * ppmDivider * 1000000) / toDemurrageAmount(demurrageModifier);
return ( _value * ppmDivider * 1000000 ) / demurrageAmount ;
2021-02-06 15:18:30 +01:00
}
2021-05-01 08:40:27 +02:00
// Implements ERC20, triggers tax and/or redistribution
2021-02-06 15:18:30 +01:00
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-05-01 08:40:27 +02:00
// Implements 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
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-06 22:04:39 +01:00
emit Transfer ( msg . sender , _to , _value ) ;
2021-02-02 16:10:21 +01:00
return result ;
}
2021-02-05 09:22:36 +01:00
2021-05-01 08:40:27 +02:00
// Implements ERC20, triggers tax and/or redistribution
2021-02-05 09:22:36 +01:00
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 ) ;
2021-02-06 22:04:39 +01:00
emit Transfer ( _from , _to , _value ) ;
2021-02-05 09:22:36 +01:00
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-05-01 08:40:27 +02:00
// Implements EIP173
function transferOwnership ( address _newOwner ) public returns ( bool ) {
require ( msg . sender == owner ) ;
newOwner = _newOwner ;
}
// Implements OwnedAccepter
function acceptOwnership ( ) public returns ( bool ) {
address oldOwner ;
require ( msg . sender == newOwner ) ;
oldOwner = owner ;
owner = newOwner ;
newOwner = address ( 0 ) ;
emit OwnershipTransferred ( oldOwner , owner ) ;
}
// Implements EIP165
function supportsInterface ( bytes4 _sum ) public pure returns ( bool ) {
if ( _sum == 0xc6bb4b70 ) { // ERC20
return true ;
}
if ( _sum == 0x449a52f8 ) { // Minter
return true ;
}
if ( _sum == 0x01ffc9a7 ) { // EIP165
return true ;
}
if ( _sum == 0x9493f8b2 ) { // EIP173
return true ;
}
if ( _sum == 0x37a47be4 ) { // OwnedAccepter
return true ;
}
return false ;
}
2021-02-02 11:26:44 +01:00
}