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 }