erc20-demurrage-token

ERC20 token with redistributed continual demurrage
Log | Files | Refs | README

DemurrageTokenSingleNocap.sol (22856B)


      1 pragma solidity >= 0.8.0;
      2 
      3 import "aux/ABDKMath64x64.sol";
      4 
      5 // SPDX-License-Identifier: AGPL-3.0-or-later
      6 
      7 contract DemurrageTokenSingleNocap {
      8 
      9 	struct redistributionItem {
     10 		uint32 period;
     11 		uint72 value;
     12 		uint64 demurrage;
     13 	}
     14 	redistributionItem[] public redistributions;
     15 
     16 	// Account balances
     17 	mapping (address => uint256) account;
     18 	
     19 	// Cached demurrage amount, ppm with 38 digit resolution
     20 	//uint128 public demurrageAmount;
     21 	int128 public demurrageAmount;
     22 
     23 	// Cached demurrage timestamp; the timestamp for which demurrageAmount was last calculated
     24 	uint256 public demurrageTimestamp;
     25 
     26 	// Implements EIP173
     27 	address public owner;
     28 
     29 	address newOwner;
     30 
     31 	// Implements ERC20
     32 	string public name;
     33 
     34 	// Implements ERC20
     35 	string public symbol;
     36 
     37 	// Implements ERC20
     38 	uint256 public immutable decimals;
     39 
     40 	uint256 supply;
     41 
     42 	// Last executed period
     43 	uint256 public lastPeriod;
     44 
     45 	// Last sink redistribution amount
     46 	uint256 public totalSink;
     47 
     48 	// Value of burnt tokens (burnt tokens do not decay)
     49 	uint256 burned;
     50 
     51 	// 128 bit resolution of the demurrage divisor
     52 	// (this constant x 1000000 is contained within 128 bits)
     53 	//uint256 constant nanoDivider = 100000000000000000000000000; // now nanodivider, 6 zeros less
     54 
     55 	// remaining decimal positions of nanoDivider to reach 38, equals precision in growth and decay
     56 	//uint256 constant growthResolutionFactor = 1000000000000;
     57 
     58 	// demurrage decimal width; 38 places
     59 	//uint256 public immutable resolutionFactor = nanoDivider * growthResolutionFactor; 
     60 
     61 	// Timestamp of start of periods (time which contract constructor was called)
     62 	uint256 public immutable periodStart;
     63 
     64 	// Duration of a single redistribution period in seconds
     65 	uint256 public immutable periodDuration;
     66 
     67 	// Demurrage in ppm per minute
     68 	//uint256 public immutable decayLevel;
     69 	// 64x64
     70 	int128 public immutable decayLevel;
     71 		
     72 	// Addresses allowed to mint new tokens
     73 	mapping (address => bool) minter;
     74 
     75 	// Storage for ERC20 approve/transferFrom methods
     76 	mapping (address => mapping (address => uint256 ) ) allowance; // holder -> spender -> amount (amount is subject to demurrage)
     77 
     78 	// Address to send unallocated redistribution tokens
     79 	address public sinkAddress; 
     80 
     81 	// timestamp when token contract expires
     82 	uint256 public expires;
     83 	bool expired;
     84 
     85 	// supply xap
     86 	uint256 public maxSupply;
     87 
     88 	// Implements ERC20
     89 	event Transfer(address indexed _from, address indexed _to, uint256 _value);
     90 
     91 	// Implements ERC20
     92 	event Approval(address indexed _owner, address indexed _spender, uint256 _value);
     93 
     94 	// Implements Minter
     95 	event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value);
     96 
     97 	// New demurrage cache milestone calculated
     98 	event Decayed(uint256 indexed _period, uint256 indexed _periodCount, int128 indexed _oldAmount, int128 _newAmount);
     99 
    100 	// When a new period threshold has been crossed
    101 	event Period(uint256 _period);
    102 
    103 	// Redistribution applied on a single eligible account
    104 	event Redistribution(address indexed _account, uint256 indexed _period, uint256 _value);
    105 
    106 	// Temporary event used in development, will be removed on prod
    107 	//event Debug(bytes32 _foo);
    108 	event Debug(int128 indexed _foo, uint256 indexed _bar);
    109 
    110 	// Implements Burn
    111 	event Burn(address indexed _burner, uint256 _value);
    112 
    113 	// EIP173
    114 	event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173
    115 
    116 	// Implements Expire
    117 	event Expired(uint256 _timestamp);
    118 
    119 	// Implements Expire
    120 	event ExpiryChange(uint256 indexed _oldTimestamp, uint256 _newTimestamp);
    121 
    122 	event Cap(uint256 indexed _oldCap, uint256 _newCap);
    123 
    124 	// Implements Seal
    125 	uint256 public sealState;
    126 	uint8 constant WRITER_STATE = 1;
    127 	uint8 constant SINK_STATE = 2;
    128 	uint8 constant EXPIRY_STATE = 4;
    129 	uint8 constant CAP_STATE = 8;
    130 	// Implements Seal
    131 	uint256 constant public maxSealState = 15;
    132 
    133 	// Implements Seal
    134 	event SealStateChange(bool indexed _final, uint256 _sealState);
    135 
    136 
    137 	constructor(string memory _name, string memory _symbol, uint8 _decimals, int128 _decayLevel, uint256 _periodMinutes, address _defaultSinkAddress) {
    138 		require(_decayLevel < (1 << 64));
    139 		redistributionItem memory initialRedistribution;
    140 
    141 		//require(ABDKMath64x64.toUInt(_decayLevel) == 0);
    142 
    143 		// ACL setup
    144 		owner = msg.sender;
    145 
    146 		// ERC20 setup
    147 		name = _name;
    148 		symbol = _symbol;
    149 		decimals = _decimals;
    150 
    151 		// Demurrage setup
    152 		demurrageTimestamp = block.timestamp;
    153 		periodStart = demurrageTimestamp;
    154 		periodDuration = _periodMinutes * 60;
    155 		demurrageAmount = ABDKMath64x64.fromUInt(1);
    156 
    157 		decayLevel = ABDKMath64x64.ln(_decayLevel);
    158 		initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1);
    159 		redistributions.push(initialRedistribution);
    160 
    161 		// Misc settings
    162 		sinkAddress = _defaultSinkAddress;
    163 	}
    164 
    165 	function seal(uint256 _state) public returns(uint256) {
    166 		require(_state < 16, 'ERR_INVALID_STATE');
    167 		require(_state & sealState == 0, 'ERR_ALREADY_LOCKED');
    168 		sealState |= _state;
    169 		emit SealStateChange(sealState == maxSealState, sealState);
    170 		return uint256(sealState);
    171 	}
    172 
    173 	function isSealed(uint256 _state) public view returns(bool) {
    174 		require(_state < maxSealState);
    175 		if (_state == 0) {
    176 			return sealState == maxSealState;
    177 		}
    178 		return _state & sealState == _state;
    179 	}
    180 
    181 	// Set when token expires. 
    182 	// Value is set it terms of redistribution periods.
    183 	// Cannot be set to a time in the past.
    184 	function setExpirePeriod(uint256 _expirePeriod) public {
    185 		uint256 r;
    186 		uint256 oldTimestamp;
    187 
    188 		require(!isSealed(EXPIRY_STATE));
    189 		require(!expired);
    190 		require(msg.sender == owner);
    191 		r = periodStart + (_expirePeriod * periodDuration);
    192 		require(r > expires);
    193 		oldTimestamp = expires;
    194 		expires = r;
    195 		emit ExpiryChange(oldTimestamp, expires);
    196 	}
    197 
    198 	// Change max token supply.
    199 	// Can only increase supply cap, not decrease.
    200 	function setMaxSupply(uint256 _cap) public {
    201 		require(!isSealed(CAP_STATE));
    202 		require(msg.sender == owner);
    203 		require(_cap > totalSupply());
    204 		emit Cap(maxSupply, _cap);
    205 		maxSupply = _cap;
    206 	}
    207 
    208 	// Change sink address for redistribution
    209 	function setSinkAddress(address _sinkAddress) public {
    210 		require(!isSealed(SINK_STATE));
    211 		require(msg.sender == owner);
    212 		sinkAddress = _sinkAddress;
    213 	}
    214 
    215 	// Expire the contract if expire is set and we have gone over the threshold.
    216 	// Finalizes demurrage up to the timestamp of the expiry. 
    217 	// The first approve, transfer or transferFrom call that hits the ex == 2 will get the tx mined. but without the actual effect. Otherwise we would have to wait until an external egent called applyExpiry to get the correct final balance.
    218 	// Implements Expire
    219 	function applyExpiry() public returns(uint8) {
    220 		if (expired) {
    221 			return 1;
    222 		}
    223 		if (expires == 0) {
    224 			return 0;
    225 		}
    226 		if (block.timestamp >= expires) {
    227 			applyDemurrageLimited(expires - demurrageTimestamp / 60);
    228 			expired = true;
    229 			emit Expired(block.timestamp);
    230 			changePeriod();
    231 			return 2;
    232 		}
    233 		return 0;
    234 	}
    235 
    236 	// Given address will be allowed to call the mintTo() function
    237 	// Implements Writer
    238 	function addWriter(address _minter) public returns (bool) {
    239 		require(!isSealed(WRITER_STATE));
    240 		require(msg.sender == owner);
    241 		minter[_minter] = true;
    242 		return true;
    243 	}
    244 
    245 	// Given address will no longer be allowed to call the mintTo() function
    246 	// Implements Writer
    247 	function deleteWriter(address _minter) public returns (bool) {
    248 		require(!isSealed(WRITER_STATE));
    249 		require(msg.sender == owner || _minter == msg.sender);
    250 		minter[_minter] = false;
    251 		return true;
    252 	}
    253 
    254 	// Implements Writer
    255 	function isWriter(address _minter) public view returns(bool) {
    256 		return minter[_minter] || _minter == owner;
    257 	}
    258 
    259 	/// Implements ERC20
    260 	function balanceOf(address _account) public view returns (uint256) {
    261 		int128 baseBalance;
    262 		int128 currentDemurragedAmount;
    263 		uint256 periodCount;
    264 
    265 		baseBalance = ABDKMath64x64.fromUInt(baseBalanceOf(_account));
    266 
    267 		periodCount = getMinutesDelta(demurrageTimestamp);
    268 
    269 		currentDemurragedAmount = ABDKMath64x64.mul(baseBalance, demurrageAmount);
    270 		return decayBy(ABDKMath64x64.toUInt(currentDemurragedAmount), periodCount);
    271 	}
    272 
    273 	// Balance unmodified by demurrage
    274 	function baseBalanceOf(address _account) public view returns (uint256) {
    275 		return account[_account];
    276 	}
    277 
    278 	/// Increases base balance for a single account
    279 	function increaseBaseBalance(address _account, uint256 _delta) private returns (bool) {
    280 		uint256 oldBalance;
    281 		uint256 workAccount;
    282 
    283 		workAccount = uint256(account[_account]);
    284 	
    285 		if (_delta == 0) {
    286 			return false;
    287 		}
    288 
    289 		oldBalance = baseBalanceOf(_account);
    290 		account[_account] = oldBalance + _delta;
    291 		return true;
    292 	}
    293 
    294 	/// Decreases base balance for a single account
    295 	function decreaseBaseBalance(address _account, uint256 _delta) private returns (bool) {
    296 		uint256 oldBalance;
    297 		uint256 workAccount;
    298 
    299 		workAccount = uint256(account[_account]);
    300 
    301 		if (_delta == 0) {
    302 			return false;
    303 		}
    304 
    305 		oldBalance = baseBalanceOf(_account);	
    306 		require(oldBalance >= _delta, 'ERR_OVERSPEND'); // overspend guard
    307 		account[_account] = oldBalance - _delta;
    308 		return true;
    309 	}
    310 
    311 	// Send full balance of one account to another
    312 	function sweep(address _account) public returns (uint256) {
    313 		uint256 v;
    314 
    315 		v = account[msg.sender];
    316 		account[msg.sender] = 0;
    317 		account[_account] += v;
    318 		emit Transfer(msg.sender, _account, v);
    319 		return v;
    320 	}
    321 
    322 	// Creates new tokens out of thin air, and allocates them to the given address
    323 	// Triggers tax
    324 	// Implements Minter
    325 	function mintTo(address _beneficiary, uint256 _amount) public returns (bool) {
    326 		uint256 baseAmount;
    327 
    328 		require(applyExpiry() == 0);
    329 		require(minter[msg.sender] || msg.sender == owner, 'ERR_ACCESS');
    330 
    331 		changePeriod();
    332 		if (maxSupply > 0) {
    333 			require(supply + _amount <= maxSupply);
    334 		}
    335 		supply += _amount;
    336 
    337 		baseAmount = toBaseAmount(_amount);
    338 		increaseBaseBalance(_beneficiary, baseAmount);
    339 		emit Mint(msg.sender, _beneficiary, _amount);
    340 		saveRedistributionSupply();
    341 		return true;
    342 	}
    343 
    344 	// Implements Minter
    345 	function mint(address _beneficiary, uint256 _amount, bytes calldata _data) public {
    346 		_data;
    347 		mintTo(_beneficiary, _amount);
    348 	}
    349 
    350 	// Implements Minter
    351 	function safeMint(address _beneficiary, uint256 _amount, bytes calldata _data) public {
    352 		_data;
    353 		mintTo(_beneficiary, _amount);
    354 	}
    355 
    356 	// Deserializes the redistribution word
    357 	function toRedistribution(uint256 _participants, int128 _demurrageModifier, uint256 _value, uint256 _period) public pure returns(redistributionItem memory) {
    358 		redistributionItem memory redistribution;
    359 
    360 		redistribution.period = uint32(_period);
    361 		redistribution.value = uint72(_value);
    362 		redistribution.demurrage = uint64(uint128(_demurrageModifier) & 0xffffffffffffffff);
    363 		_participants;
    364 		return redistribution;
    365 
    366 	}
    367 
    368 	// Serializes the demurrage period part of the redistribution word
    369 	function toRedistributionPeriod(redistributionItem memory _redistribution) public pure returns (uint256) {
    370 		return uint256(_redistribution.period);
    371 	}
    372 
    373 	// Serializes the supply part of the redistribution word
    374 	function toRedistributionSupply(redistributionItem memory _redistribution) public pure returns (uint256) {
    375 		return uint256(_redistribution.value);
    376 	}
    377 
    378 	// Serializes the number of participants part of the redistribution word
    379 	function toRedistributionDemurrageModifier(redistributionItem memory _redistribution) public pure returns (int128) {
    380 		int128 r;
    381 
    382 		r = int128(int64(_redistribution.demurrage) & int128(0x0000000000000000ffffffffffffffff));
    383 		if (r == 0) {
    384 			r = ABDKMath64x64.fromUInt(1);
    385 		}
    386 		return r;
    387 	}
    388 
    389 	// Client accessor to the redistributions array length
    390 	function redistributionCount() public view returns (uint256) {
    391 		return redistributions.length;
    392 	}
    393 
    394 	// Save the current total supply amount to the current redistribution period
    395 	function saveRedistributionSupply() private returns (bool) {
    396 		redistributionItem memory currentRedistribution;
    397 		uint256 grownSupply;
    398 
    399 		grownSupply = totalSupply();
    400 		currentRedistribution = redistributions[redistributions.length-1];
    401 		currentRedistribution.value = uint72(grownSupply);
    402 
    403 		redistributions[redistributions.length-1] = currentRedistribution;
    404 		return true;
    405 	}
    406 
    407 	// Get the demurrage period of the current block number
    408 	function actualPeriod() public view returns (uint128) {
    409 		return uint128((block.timestamp - periodStart) / periodDuration + 1);
    410 	}
    411 
    412 	// Retrieve next redistribution if the period threshold has been crossed
    413 	function checkPeriod() private view returns (redistributionItem memory) {
    414 		redistributionItem memory lastRedistribution;
    415 		redistributionItem memory emptyRedistribution;
    416 		uint256 currentPeriod;
    417 
    418 		lastRedistribution =  redistributions[lastPeriod];
    419 		currentPeriod = this.actualPeriod();
    420 		if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) {
    421 			return emptyRedistribution;
    422 		}
    423 		return lastRedistribution;
    424 	}
    425 
    426 	function getDistribution(uint256 _supply, int128 _demurrageAmount) public pure returns (uint256) {
    427 		int128 difference;
    428 
    429 		difference = ABDKMath64x64.mul(ABDKMath64x64.fromUInt(_supply), ABDKMath64x64.sub(ABDKMath64x64.fromUInt(1), _demurrageAmount));
    430 		return _supply - ABDKMath64x64.toUInt(difference);
    431 			
    432 	}
    433 
    434 	function getDistributionFromRedistribution(redistributionItem memory _redistribution) public pure returns (uint256) {
    435 		uint256 redistributionSupply;
    436 		int128 redistributionDemurrage;
    437 
    438 		redistributionSupply = toRedistributionSupply(_redistribution);
    439 		redistributionDemurrage = toRedistributionDemurrageModifier(_redistribution);
    440 		return getDistribution(redistributionSupply, redistributionDemurrage);
    441 	}
    442 
    443 	// Returns the amount sent to the sink address
    444 	function applyDefaultRedistribution(redistributionItem memory _redistribution) private returns (uint256) {
    445 		uint256 unit;
    446 		uint256 baseUnit;
    447 	
    448 		unit = totalSupply() - getDistributionFromRedistribution(_redistribution);	
    449 		baseUnit = toBaseAmount(unit) - totalSink;
    450 		increaseBaseBalance(sinkAddress, baseUnit);
    451 		emit Redistribution(sinkAddress, _redistribution.period, unit);
    452 		lastPeriod += 1;
    453 		totalSink += baseUnit;
    454 		return unit;
    455 	}
    456 
    457 	// Recalculate the demurrage modifier for the new period
    458 	// Note that the supply for the consecutive period will be taken at the time of code execution, and thus not necessarily at the time when the redistribution period threshold was crossed.
    459 	function changePeriod() public returns (bool) {
    460 		redistributionItem memory currentRedistribution;
    461 		redistributionItem memory nextRedistribution;
    462 		redistributionItem memory lastRedistribution;
    463 		uint256 currentPeriod;
    464 		int128 lastDemurrageAmount;
    465 		int128 nextRedistributionDemurrage;
    466 		uint256 demurrageCounts;
    467 		uint256 nextPeriod;
    468 
    469 		applyDemurrage();
    470 		currentRedistribution = checkPeriod();
    471 		if (isEmptyRedistribution(currentRedistribution)) {
    472 			return false;
    473 		}
    474 
    475 		// calculate the decay from previous redistributino
    476 		lastRedistribution = redistributions[lastPeriod];
    477 		currentPeriod = toRedistributionPeriod(currentRedistribution);
    478 		nextPeriod = currentPeriod + 1;
    479 		lastDemurrageAmount = toRedistributionDemurrageModifier(lastRedistribution);
    480 		demurrageCounts = (periodDuration * currentPeriod) / 60;
    481 		// TODO refactor decayby to take int128 then DRY with it
    482 		nextRedistributionDemurrage = ABDKMath64x64.exp(ABDKMath64x64.mul(decayLevel, ABDKMath64x64.fromUInt(demurrageCounts)));
    483 		nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply(), nextPeriod);
    484 		redistributions.push(nextRedistribution);
    485 
    486 		applyDefaultRedistribution(nextRedistribution);
    487 		emit Period(nextPeriod);
    488 		return true;
    489 	}
    490 
    491 	// Calculate the time delta in whole minutes passed between given timestamp and current timestamp
    492 	function getMinutesDelta(uint256 _lastTimestamp) public view returns (uint256) {
    493 		return (block.timestamp - _lastTimestamp) / 60;
    494 	}
    495 
    496 	// Calculate and cache the demurrage value corresponding to the (period of the) time of the method call
    497 	function applyDemurrage() public returns (uint256) {
    498 		return applyDemurrageLimited(0);
    499 	}
    500 
    501 	// returns true if expired
    502 	function applyDemurrageLimited(uint256 _rounds) public returns (uint256) {
    503 		int128 v;
    504 		uint256 periodCount;
    505 		int128 periodPoint;
    506 		int128 lastDemurrageAmount;
    507 
    508 		if (expired) {
    509 			return 0; 
    510 		}
    511 
    512 		periodCount = getMinutesDelta(demurrageTimestamp);
    513 		if (periodCount == 0) {
    514 			return 0;
    515 		}
    516 		lastDemurrageAmount = demurrageAmount;
    517 	
    518 		// safety limit for exponential calculation to ensure that we can always
    519 		// execute this code no matter how much time passes.			
    520 		if (_rounds > 0 && _rounds < periodCount) {
    521 			periodCount = _rounds;
    522 		}
    523 
    524 		periodPoint = ABDKMath64x64.fromUInt(periodCount);
    525 		v = ABDKMath64x64.mul(decayLevel, periodPoint);
    526 		v = ABDKMath64x64.exp(v);
    527 		demurrageAmount = ABDKMath64x64.mul(demurrageAmount, v);
    528 
    529 		demurrageTimestamp = demurrageTimestamp + (periodCount * 60);
    530 		emit Decayed(demurrageTimestamp, periodCount, lastDemurrageAmount, demurrageAmount);
    531 		return periodCount;
    532 	}
    533 
    534 	// Return timestamp of start of period threshold
    535 	function getPeriodTimeDelta(uint256 _periodCount) public view returns (uint256) {
    536 		return periodStart + (_periodCount * periodDuration);
    537 	}
    538 
    539 	// Amount of demurrage cycles inbetween the current timestamp and the given target time
    540 	function demurrageCycles(uint256 _target) public view returns (uint256) {
    541 		return (block.timestamp - _target) / 60;
    542 	}
    543 
    544 	// Equality check for empty redistribution data
    545 	function isEmptyRedistribution(redistributionItem memory _redistribution) public pure returns(bool) {
    546 		if (_redistribution.period > 0) {
    547 			return false;
    548 		}
    549 		if (_redistribution.value > 0) {
    550 			return false;
    551 		}
    552 		if (_redistribution.demurrage > 0) {
    553 			return false;
    554 		}
    555 		return true;
    556 	}
    557 
    558 
    559 	// Calculate a value reduced by demurrage by the given period
    560 	function decayBy(uint256 _value, uint256 _period)  public view returns (uint256) {
    561 		int128 valuePoint;
    562 		int128 periodPoint;
    563 		int128 v;
    564 	
    565 		valuePoint = ABDKMath64x64.fromUInt(_value);
    566 		periodPoint = ABDKMath64x64.fromUInt(_period);
    567 
    568 		v = ABDKMath64x64.mul(decayLevel, periodPoint);
    569 		v = ABDKMath64x64.exp(v);
    570 		v = ABDKMath64x64.mul(valuePoint, v);
    571 		return ABDKMath64x64.toUInt(v);
    572 	}
    573 
    574 
    575 	// Inflates the given amount according to the current demurrage modifier
    576 	function toBaseAmount(uint256 _value) public view returns (uint256) {
    577 		int128 r;
    578 		r = ABDKMath64x64.div(ABDKMath64x64.fromUInt(_value), demurrageAmount);
    579 		return ABDKMath64x64.toUInt(r);
    580 	}
    581 
    582 	// Triggers tax and/or redistribution
    583 	// Implements ERC20
    584 	function approve(address _spender, uint256 _value) public returns (bool) {
    585 		uint256 baseValue;
    586 		uint8 ex;
    587 
    588 		ex = applyExpiry();
    589 		if (ex == 2) {
    590 			return false;	
    591 		} else if (ex > 0) {
    592 			revert('EXPIRED');
    593 		}
    594 		if (allowance[msg.sender][_spender] > 0) {
    595 			require(_value == 0, 'ZERO_FIRST');
    596 		}
    597 		
    598 		changePeriod();
    599 
    600 		baseValue = toBaseAmount(_value);
    601 		allowance[msg.sender][_spender] = baseValue;
    602 		emit Approval(msg.sender, _spender, _value);
    603 		return true;
    604 	}
    605 
    606 	// Reduce allowance by amount
    607 	function decreaseAllowance(address _spender, uint256 _value) public returns (bool) {
    608 		uint256 baseValue;
    609 
    610 		baseValue = toBaseAmount(_value);
    611 		require(allowance[msg.sender][_spender] >= baseValue);
    612 		
    613 		changePeriod();
    614 
    615 		allowance[msg.sender][_spender] -= baseValue;
    616 		emit Approval(msg.sender, _spender, allowance[msg.sender][_spender]);
    617 		return true;
    618 	}
    619 
    620 	// Increase allowance by amount
    621 	function increaseAllowance(address _spender, uint256 _value) public returns (bool) {
    622 		uint256 baseValue;
    623 
    624 		changePeriod();
    625 
    626 		baseValue = toBaseAmount(_value);
    627 
    628 		allowance[msg.sender][_spender] += baseValue;
    629 		emit Approval(msg.sender, _spender, allowance[msg.sender][_spender]);
    630 		return true;
    631 	}
    632 
    633 	// Triggers tax and/or redistribution
    634 	// Implements ERC20
    635 	function transfer(address _to, uint256 _value) public returns (bool) {
    636 		uint256 baseValue;
    637 		bool result;
    638 		uint8 ex;
    639 
    640 		ex = applyExpiry();
    641 		if (ex == 2) {
    642 			return false;	
    643 		} else if (ex > 0) {
    644 			revert('EXPIRED');
    645 		}
    646 		changePeriod();
    647 
    648 		baseValue = toBaseAmount(_value);
    649 		result = transferBase(msg.sender, _to, baseValue);
    650 		emit Transfer(msg.sender, _to, _value);
    651 		return result;
    652 	}
    653 
    654 	// Triggers tax and/or redistribution
    655 	// Implements ERC20
    656 	function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
    657 		uint256 baseValue;
    658 		bool result;
    659 		uint8 ex;
    660 
    661 		ex = applyExpiry();
    662 		if (ex == 2) {
    663 			return false;	
    664 		} else if (ex > 0) {
    665 			revert('EXPIRED');
    666 		}
    667 		changePeriod();
    668 
    669 		baseValue = toBaseAmount(_value);
    670 		require(allowance[_from][msg.sender] >= baseValue);
    671 
    672 		allowance[_from][msg.sender] -= baseValue;
    673 		result = transferBase(_from, _to, baseValue);
    674 
    675 		emit Transfer(_from, _to, _value);
    676 		return result;
    677 	}
    678 
    679 	// ERC20 transfer backend for transfer, transferFrom
    680 	function transferBase(address _from, address _to, uint256 _value) private returns (bool) {
    681 		decreaseBaseBalance(_from, _value);
    682 		increaseBaseBalance(_to, _value);
    683 
    684 		return true;
    685 	}
    686 
    687 	// Implements EIP173
    688 	function transferOwnership(address _newOwner) public returns (bool) {
    689 		address oldOwner;
    690 
    691 		require(msg.sender == owner);
    692 		oldOwner = owner;
    693 		owner = _newOwner;
    694 
    695 		emit OwnershipTransferred(oldOwner, owner);
    696 		return true;
    697 	}
    698 
    699 	// Explicitly and irretrievably burn tokens
    700 	// Only token minters can burn tokens
    701 	// Implements Burner
    702 	function burn(uint256 _value) public returns(bool) {
    703 		require(applyExpiry() == 0);
    704 		require(minter[msg.sender] || msg.sender == owner, 'ERR_ACCESS');
    705 		require(_value <= account[msg.sender]);
    706 		uint256 _delta = toBaseAmount(_value);
    707 
    708 		//applyDemurrage();
    709 		decreaseBaseBalance(msg.sender, _delta);
    710 		burned += _value;
    711 		emit Burn(msg.sender, _value);
    712 		return true;
    713 	}
    714 
    715 	// Implements Burner
    716 	function burn(address _from, uint256 _value, bytes calldata _data) public {
    717 		require(_from == msg.sender, 'ERR_ONLY_SELF_BURN');
    718 		_data;
    719 		burn(_value);
    720 	}
    721 
    722 	// Implements Burner
    723 	function burn() public returns(bool) {
    724 		return burn(account[msg.sender]);
    725 	}
    726 
    727 	// Implements ERC20
    728 	function totalSupply() public view returns (uint256) {
    729 		return supply - burned;
    730 	}
    731 
    732 	// Return total number of burned tokens
    733 	// Implements Burner
    734 	function totalBurned() public view returns (uint256) {
    735 		return burned;
    736 	}
    737 
    738 	// Return total number of tokens ever minted
    739 	// Implements Burner
    740 	function totalMinted() public view returns (uint256) {
    741 		return supply;
    742 	}
    743 
    744 
    745 	// Implements EIP165
    746 	function supportsInterface(bytes4 _sum) public pure returns (bool) {
    747 		if (_sum == 0xb61bc941) { // ERC20
    748 			return true;
    749 		}
    750 		if (_sum == 0x5878bcf4) { // Minter
    751 			return true;
    752 		}
    753 		if (_sum == 0xbc4babdd) { // Burner
    754 			return true;
    755 		}
    756 		if (_sum == 0x0d7491f8) { // Seal
    757 			return true;
    758 		}
    759 		if (_sum == 0xabe1f1f5) { // Writer
    760 			return true;
    761 		}
    762 		if (_sum == 0x841a0e94) { // Expire
    763 			return true;
    764 		}
    765 		if (_sum == 0x01ffc9a7) { // ERC165
    766 			return true;
    767 		}
    768 		if (_sum == 0x9493f8b2) { // ERC173
    769 			return true;
    770 		}
    771 		if (_sum == 0xd0017968) { // ERC5678Ext20
    772 			return true;
    773 		}
    774 		return false;
    775 	}
    776 }