Rewrite to use timestamp instead of blocks

This commit is contained in:
nolash
2021-02-06 15:17:37 +01:00
parent 50c9cb61b5
commit 2ea28aacd1
5 changed files with 210 additions and 148 deletions

View File

@@ -11,11 +11,12 @@ contract RedistributedDemurrageToken {
uint256 public decimals;
uint256 public totalSupply;
uint256 public minimumParticipantSpend;
uint256 constant ppmDivider = 100000000000000000000000000000000;
uint256 public periodStart;
uint256 public periodDuration;
uint256 public taxLevel; // PPM
uint256 public demurrageModifier; // PPM
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)
bytes32[] public redistributions; // uint1(isFractional) | uint1(unused) | uint38(participants) | uint160(value) | uint56(period)
mapping (address => bytes32) account; // uint20(unused) | uint56(period) | uint160(value)
@@ -28,19 +29,20 @@ contract RedistributedDemurrageToken {
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value);
//event Debug(uint256 _foo);
event Taxed(uint256 indexed _period, uint256 remainder);
event Decayed(uint256 indexed _period, uint256 indexed _periodCount, uint256 indexed _oldAmount, uint256 _newAmount);
event Redistribution(address indexed _account, uint256 indexed _period, uint256 _value);
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint32 _taxLevel, uint256 _period, address _defaultSinkAddress) public {
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _taxLevelMinute, uint256 _periodMinutes, address _defaultSinkAddress) public {
owner = msg.sender;
minter[owner] = true;
periodStart = block.number;
periodDuration = _period;
taxLevel = _taxLevel;
periodStart = block.timestamp;
periodDuration = _periodMinutes * 60;
name = _name;
symbol = _symbol;
decimals = _decimals;
demurrageModifier = 1000000;
demurrageModifier = ppmDivider * 1000000; // Emulates 38 decimal places
demurrageModifier |= (1 << 128);
taxLevel = _taxLevelMinute; // 38 decimal places
sinkAddress = _defaultSinkAddress;
bytes32 initialRedistribution = toRedistribution(0, 0, 1);
redistributions.push(initialRedistribution);
@@ -53,13 +55,24 @@ contract RedistributedDemurrageToken {
minter[_minter] = true;
return true;
}
/// ERC20
function balanceOf(address _account) public view returns (uint256) {
uint256 baseBalance = getBaseBalance(_account);
uint256 inverseModifier = 1000000 - demurrageModifier;
uint256 balanceModifier = (inverseModifier * baseBalance) / 1000000;
return baseBalance - balanceModifier;
uint256 baseBalance;
uint256 anchorDemurrageAmount;
uint256 anchorDemurragePeriod;
uint256 currentDemurrageAmount;
uint256 periodCount;
baseBalance = getBaseBalance(_account);
anchorDemurrageAmount = toDemurrageAmount(demurrageModifier);
anchorDemurragePeriod = toDemurragePeriod(demurrageModifier);
periodCount = actualPeriod() - toDemurragePeriod(demurrageModifier);
currentDemurrageAmount = toTaxPeriodAmount(anchorDemurrageAmount, periodCount);
return (baseBalance * currentDemurrageAmount) / (ppmDivider * 1000000);
}
/// Balance unmodified by demurrage
@@ -71,7 +84,11 @@ contract RedistributedDemurrageToken {
function increaseBaseBalance(address _account, uint256 _delta) private returns (bool) {
uint256 oldBalance;
uint256 newBalance;
if (_delta == 0) {
return false;
}
oldBalance = getBaseBalance(_account);
newBalance = oldBalance + _delta;
require(uint160(newBalance) > uint160(oldBalance), 'ERR_WOULDWRAP'); // revert if increase would result in a wrapped value
@@ -85,6 +102,10 @@ contract RedistributedDemurrageToken {
uint256 oldBalance;
uint256 newBalance;
if (_delta == 0) {
return false;
}
oldBalance = getBaseBalance(_account);
require(oldBalance >= _delta, 'ERR_OVERSPEND'); // overspend guard
newBalance = oldBalance - _delta;
@@ -98,8 +119,8 @@ contract RedistributedDemurrageToken {
function mintTo(address _beneficiary, uint256 _amount) external returns (bool) {
require(minter[msg.sender]);
// TODO: get base amount for minting
applyTax();
applyDemurrage();
changePeriod();
totalSupply += _amount;
increaseBaseBalance(_beneficiary, _amount);
emit Mint(msg.sender, _beneficiary, _amount);
@@ -164,7 +185,7 @@ contract RedistributedDemurrageToken {
// Get the demurrage period of the current block number
function actualPeriod() public view returns (uint256) {
return (block.number - periodStart) / periodDuration + 1;
return (block.timestamp - periodStart) / periodDuration + 1;
}
// Add an entered demurrage period to the redistribution array
@@ -228,7 +249,7 @@ contract RedistributedDemurrageToken {
redistributions[redistributionPeriod-1] |= 0x8000000001000000000000000000000000000000000000000000000000000000;
}
increaseBaseBalance(sinkAddress, unit); //truncatedResult);
increaseBaseBalance(sinkAddress, unit / ppmDivider); //truncatedResult);
return unit;
}
@@ -248,9 +269,40 @@ contract RedistributedDemurrageToken {
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);
newDemurrageAmount = toTaxPeriodAmount(lastDemurrageAmount, periodCount);
demurrageModifier = 0;
demurrageModifier |= (newDemurrageAmount & 0x00000000000000000000000000000000ffffffffffffffffffffffffffffffff);
demurrageModifier |= (epochPeriodCount << 128);
emit Decayed(epochPeriodCount, periodCount, lastDemurrageAmount, newDemurrageAmount);
return true;
}
// Recalculate the demurrage modifier for the new period
// After this, all REPORTED balances will have been reduced by the corresponding ratio (but the effecive totalsupply stays the same)
function applyTax() public returns (uint256) {
//function applyTax() public returns (uint256) {
function changePeriod() public returns (uint256) {
bytes32 currentRedistribution;
bytes32 nextRedistribution;
uint256 currentPeriod;
@@ -261,7 +313,7 @@ contract RedistributedDemurrageToken {
if (currentRedistribution == bytes32(0x00)) {
return demurrageModifier;
}
demurrageModifier -= (demurrageModifier * taxLevel) / 1000000;
//demurrageModifier -= (demurrageModifier * taxLevel) / 1000000;
currentPeriod = toRedistributionPeriod(currentRedistribution);
nextRedistribution = toRedistribution(0, totalSupply, currentPeriod + 1);
redistributions.push(nextRedistribution);
@@ -273,19 +325,20 @@ contract RedistributedDemurrageToken {
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);
}
emit Taxed(currentPeriod, currentRemainder);
return demurrageModifier;
}
// Calculate a value reduced by demurrage by the given period
function toTaxPeriodAmount(uint256 _value, uint256 _period) public view returns (uint256) {
uint256 valueFactor;
uint256 truncatedTaxLevel;
// TODO: doesn't work for solidity as floats are missing and using ints linearly increases the order of magnitude
// valueFactor = 1000000 * (((1000000-taxLevel)/1000000) ** _period);
// 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 * taxLevel) / 1000000);
valueFactor = valueFactor - ((valueFactor * truncatedTaxLevel) / 1000000);
}
return (valueFactor * _value) / 1000000;
}
@@ -311,7 +364,7 @@ contract RedistributedDemurrageToken {
}
supply = toRedistributionSupply(periodRedistribution);
baseValue = ((supply / participants) * (taxLevel) / 1000000);
baseValue = ((supply / participants) * (taxLevel / 1000000)) / ppmDivider;
value = toTaxPeriodAmount(baseValue, period - 1);
account[_account] &= bytes32(0x000000000000000000000000ffffffffffffffffffffffffffffffffffffffff);
@@ -323,7 +376,21 @@ contract RedistributedDemurrageToken {
// Inflates the given amount according to the current demurrage modifier
function toBaseAmount(uint256 _value) public view returns (uint256) {
return (_value * 1000000) / demurrageModifier;
return (_value * ppmDivider * 1000000) / toDemurrageAmount(demurrageModifier);
}
// ERC20, triggers tax and/or redistribution
function approve(address _spender, uint256 _value) public returns (bool) {
uint256 baseValue;
applyDemurrage();
changePeriod();
applyRedistributionOnAccount(msg.sender);
baseValue = toBaseAmount(_value);
allowance[msg.sender][_spender] += baseValue;
emit Approval(msg.sender, _spender, _value);
return true;
}
// ERC20, triggers tax and/or redistribution
@@ -331,7 +398,8 @@ contract RedistributedDemurrageToken {
uint256 baseValue;
bool result;
applyTax();
applyDemurrage();
changePeriod();
applyRedistributionOnAccount(msg.sender);
// TODO: Prefer to truncate the result, instead it seems to round to nearest :/
@@ -341,6 +409,23 @@ contract RedistributedDemurrageToken {
return result;
}
// ERC20, triggers tax and/or redistribution
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
uint256 baseValue;
bool result;
applyDemurrage();
changePeriod();
applyRedistributionOnAccount(msg.sender);
baseValue = toBaseAmount(_value);
require(allowance[_from][msg.sender] >= baseValue);
result = transferBase(_from, _to, baseValue);
return result;
}
// ERC20 transfer backend for transfer, transferFrom
function transferBase(address _from, address _to, uint256 _value) private returns (bool) {
uint256 period;
@@ -354,32 +439,4 @@ contract RedistributedDemurrageToken {
}
return true;
}
// ERC20, triggers tax and/or redistribution
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
uint256 baseValue;
bool result;
applyTax();
applyRedistributionOnAccount(msg.sender);
baseValue = toBaseAmount(_value);
require(allowance[_from][msg.sender] >= baseValue);
result = transferBase(_from, _to, baseValue);
return result;
}
// ERC20, triggers tax and/or redistribution
function approve(address _spender, uint256 _value) public returns (bool) {
uint256 baseValue;
applyTax();
applyRedistributionOnAccount(msg.sender);
baseValue = toBaseAmount(_value);
allowance[msg.sender][_spender] += baseValue;
emit Approval(msg.sender, _spender, _value);
return true;
}
}