EthereumEthereum
0xf5...eaa0
CACHE Gold

CACHE Gold

CGT

代币
市值
$1.00
 
价格
2%
此合同的源代码已经过验证!
合同元数据
编译器
0.5.16+commit.9c3226ce
语言
Solidity
合同源代码
文件 1 的 1:CacheGold.sol
pragma solidity 0.5.16;

// File: openzeppelin-solidity/contracts/ownership/Ownable.sol
/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be aplied to your functions to restrict their use to
 * the owner.
 */
contract Ownable {
    address private _owner;
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor () internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), _owner);
    }
    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }
    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner(), "Ownable: caller is not the owner");
        _;
    }
    /**
     * @dev Returns true if the caller is the current owner.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _owner;
    }
    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * > Note: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }
    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }
    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}
// File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol
/**
 * @dev Interface of the ERC20 standard as defined in the EIP. Does not include
 * the optional functions; to access them see `ERC20Detailed`.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);
    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);
    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a `Transfer` event.
     */
    function transfer(address recipient, uint256 amount) external returns (bool);
    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through `transferFrom`. This is
     * zero by default.
     *
     * This value changes when `approve` or `transferFrom` are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);
    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * > Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an `Approval` event.
     */
    function approve(address spender, uint256 amount) external returns (bool);
    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a `Transfer` event.
     */
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);
    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to `approve`. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);
}
// File: openzeppelin-solidity/contracts/math/SafeMath.sol
/**
 * @dev Wrappers over Solidity's arithmetic operations with added overflow
 * checks.
 *
 * Arithmetic operations in Solidity wrap on overflow. This can easily result
 * in bugs, because programmers usually assume that an overflow raises an
 * error, which is the standard behavior in high level programming languages.
 * `SafeMath` restores this intuition by reverting the transaction when an
 * operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");
        return c;
    }
    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;
        return c;
    }
    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }
        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");
        return c;
    }
    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, "SafeMath: division by zero");
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold
        return c;
    }
    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0, "SafeMath: modulo by zero");
        return a % b;
    }
}
// File: contracts/LockedGoldOracle.sol
// Simple contract regulating the total supply of gold locked at any
// given time so that the Cache contract can't over mint tokens
contract LockedGoldOracle is Ownable {
  using SafeMath for uint256;
  uint256 private _lockedGold;
  address private _cacheContract;
  event LockEvent(uint256 amount);
  event UnlockEvent(uint256 amount);
  function setCacheContract(address cacheContract) external onlyOwner {
    _cacheContract = cacheContract;
  }
  function lockAmount(uint256 amountGrams) external onlyOwner {
    _lockedGold = _lockedGold.add(amountGrams);
    emit LockEvent(amountGrams);
  }
  // Can only unlock amount of gold if it would leave the
  // total amount of locked gold greater than or equal to the
  // number of tokens in circulation
  function unlockAmount(uint256 amountGrams) external onlyOwner {
    _lockedGold = _lockedGold.sub(amountGrams);
    require(_lockedGold >= CacheGold(_cacheContract).totalCirculation());
    emit UnlockEvent(amountGrams);
  }
  function lockedGold() external view returns(uint256) {
    return _lockedGold;
  }
  function cacheContract() external view returns(address) {
    return _cacheContract;
  }
}
// File: contracts/CacheGold.sol
/// @title The CacheGold Token Contract
/// @author Cache Pte Ltd
contract CacheGold is IERC20, Ownable {
  using SafeMath for uint256;
  // ERC20 Detailed Info
  /* solhint-disable */
  string public constant name = "CACHE Gold";
  string public constant symbol = "CGT";
  uint8 public constant decimals = 8;
  /* solhint-enable */
  // 10^8 shortcut
  uint256 private constant TOKEN = 10 ** uint256(decimals);
  // Seconds in a day
  uint256 private constant DAY = 86400;
  // Days in a year
  uint256 private constant YEAR = 365;
  // The maximum transfer fee is 10 basis points
  uint256 private constant MAX_TRANSFER_FEE_BASIS_POINTS = 10;
  // Basis points means divide by 10,000 to get decimal
  uint256 private constant BASIS_POINTS_MULTIPLIER = 10000;
  // The storage fee of 0.25%
  uint256 private constant STORAGE_FEE_DENOMINATOR = 40000000000;
  // The inactive fee of 0.50%
  uint256 private constant INACTIVE_FEE_DENOMINATOR = 20000000000;
  // The minimum balance that would accrue a storage fee after 1 day
  uint256 private constant MIN_BALANCE_FOR_FEES = 146000;
  // Initial basis points for transfer fee
  uint256 private _transferFeeBasisPoints = 10;
  // Cap on total number of tokens that can ever be produced
  uint256 public constant SUPPLY_CAP = 8133525786 * TOKEN;
  // How many days need to pass before late fees can be collected (3 years)
  uint256 public constant INACTIVE_THRESHOLD_DAYS = 1095;
  // Token balance of each address
  mapping (address => uint256) private _balances;
  // Allowed transfer from address
  mapping (address => mapping (address => uint256)) private _allowances;
  // Last time storage fee was paid
  mapping (address => uint256) private _timeStorageFeePaid;
  // Last time the address produced a transaction on this contract
  mapping (address => uint256) private _timeLastActivity;
  // Amount of inactive fees already paid
  mapping (address => uint256) private _inactiveFeePaid;
  // If address doesn't have any activity for INACTIVE_THRESHOLD_DAYS
  // we can start deducting chunks off the address so that
  // full balance can be recouped after 200 years. This is likely
  // to happen if the user loses their private key.
  mapping (address => uint256) private _inactiveFeePerYear;
  // Addresses not subject to transfer fees
  mapping (address => bool) private _transferFeeExempt;
  // Address is not subject to storage fees
  mapping (address => bool) private _storageFeeExempt;
  // Save grace period on storage fees for an address
  mapping (address => uint256) private _storageFeeGracePeriod;
  // Current total number of tokens created
  uint256 private _totalSupply;
  // Address where storage and transfer fees are collected
  address private _feeAddress;
  // The address for the "backed treasury". When a bar is locked into the
  // vault for tokens to be minted, they are created in the backed_treasury
  // and can then be sold from this address.
  address private _backedTreasury;
  // The address for the "unbacked treasury". The unbacked treasury is a
  // storing address for excess tokens that are not locked in the vault
  // and therefore do not correspond to any real world value. If new bars are
  // locked in the vault, tokens will first be moved from the unbacked
  // treasury to the backed treasury before minting new tokens.
  //
  // This address only accepts transfers from the _backedTreasury or _redeemAddress
  // the general public should not be able to manipulate this balance.
  address private _unbackedTreasury;
  // The address for the LockedGoldOracle that determines the maximum number of
  // tokens that can be in circulation at any given time
  address private _oracle;
  // A fee-exempt address that can be used to collect gold tokens in exchange
  // for redemption of physical gold
  address private _redeemAddress;
  // An address that can force addresses with overdue storage or inactive fee to pay.
  // This is separate from the contract owner, because the owner will change
  // to a multisig address after deploy, and we want to be able to write
  // a script that can sign "force-pay" transactions with a single private key
  address private _feeEnforcer;
  // Grace period before storage fees kick in
  uint256 private _storageFeeGracePeriodDays = 0;
  // When gold bars are locked, we add tokens to circulation either
  // through moving them from the unbacked treasury or minting new ones,
  // or some combination of both
  event AddBackedGold(uint256 amount);
  // Before gold bars can be unlocked (removed from circulation), they must
  // be moved to the unbacked treasury, we emit an event when this happens
  // to signal a change in the circulating supply
  event RemoveGold(uint256 amount);
  // When an account has no activity for INACTIVE_THRESHOLD_DAYS
  // it will be flagged as inactive
  event AccountInactive(address indexed account, uint256 feePerYear);
  // If an previoulsy dormant account is reactivated
  event AccountReActive(address indexed account);
  /**
   * @dev Contructor for the CacheGold token sets internal addresses
   * @param unbackedTreasury The address of the unbacked treasury
   * @param backedTreasury The address of the backed treasury
   * @param feeAddress The address where fees are collected
   * @param redeemAddress The address where tokens are send to redeem physical gold
   * @param oracle The address of the LockedGoldOracle
   */
  constructor(address unbackedTreasury,
              address backedTreasury,
              address feeAddress,
              address redeemAddress,
              address oracle) public {
    _unbackedTreasury = unbackedTreasury;
    _backedTreasury = backedTreasury;
    _feeAddress = feeAddress;
    _redeemAddress = redeemAddress;
    _feeEnforcer = owner();
    _oracle = oracle;
    setFeeExempt(_feeAddress);
    setFeeExempt(_redeemAddress);
    setFeeExempt(_backedTreasury);
    setFeeExempt(_unbackedTreasury);
    setFeeExempt(owner());
  }
  /**
   * @dev Throws if called by any account other than THE ENFORCER
   */
  modifier onlyEnforcer() {
    require(msg.sender == _feeEnforcer);
    _;
  }
  /**
  * @dev Transfer token for a specified address
  * @param to The address to transfer to.
  * @param value The amount to be transferred.
  */
  function transfer(address to, uint256 value) external returns (bool) {
    // Update activity for the sender
    _updateActivity(msg.sender);
    // Can opportunistically mark an account inactive if someone
    // sends money to it
    if (_shouldMarkInactive(to)) {
      _setInactive(to);
    }
    _transfer(msg.sender, to, value);
    return true;
  }
  /**
  * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
  * Beware that changing an allowance with this method brings the risk that someone may use both the old
  * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
  * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
  * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
  * @param spender The address which will spend the funds.
  * @param value The amount of tokens to be spent.
  */
  function approve(address spender, uint256 value) external returns (bool) {
    _updateActivity(msg.sender);
    _approve(msg.sender, spender, value);
    return true;
  }
  /**
  * @dev Transfer tokens from one address to another.
  * Note that while this function emits an Approval event, this is not required as per the specification,
  * and other compliant implementations may not emit the event.
  * Also note that even though balance requirements are not explicitly checked,
  * any transfer attempt over the approved amount will automatically fail due to
  * SafeMath revert when trying to subtract approval to a negative balance
  * @param from address The address which you want to send tokens from
  * @param to address The address which you want to transfer to
  * @param value uint256 the amount of tokens to be transferred
  */
  function transferFrom(address from, address to, uint256 value) external returns (bool) {
    _updateActivity(msg.sender);
    _transfer(from, to, value);
    _approve(from, msg.sender, _allowances[from][msg.sender].sub(value));
    return true;
  }
  /**
  * @dev Increase the amount of tokens that an owner allowed to a spender.
  * approve should be called when allowed_[_spender] == 0. To increment
  * allowed value is better to use this function to avoid 2 calls (and wait until
  * the first transaction is mined)
  * From MonolithDAO Token.sol
  * Emits an Approval event.
  * @param spender The address which will spend the funds.
  * @param addedValue The amount of tokens to increase the allowance by.
  */
  function increaseAllowance(address spender, uint256 addedValue) external returns (bool) {
    _updateActivity(msg.sender);
    _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue));
    return true;
  }
  /**
  * @dev Decrease the amount of tokens that an owner allowed to a spender.
  * approve should be called when allowed_[_spender] == 0. To decrement
  * allowed value is better to use this function to avoid 2 calls (and wait until
  * the first transaction is mined)
  * From MonolithDAO Token.sol
  * Emits an Approval event.
  * @param spender The address which will spend the funds.
  * @param subtractedValue The amount of tokens to decrease the allowance by.
  */
  function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool) {
    _updateActivity(msg.sender);
    _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
    return true;
  }
  /**
  * @dev Function to add a certain amount of backed tokens. This will first
  * take any tokens from the _unbackedTreasury address and move them to the
  * _backedTreasury. Any remaining tokens will actually be minted.
  * This operation will fail if there is not a sufficient supply of locked gold
  * as determined by the LockedGoldOracle
  *
  * @param value The amount of tokens to add to the backed treasury
  * @return A boolean that indicates if the operation was successful.
  */
  function addBackedTokens(uint256 value) external onlyOwner returns (bool)
  {
    uint256 unbackedBalance = _balances[_unbackedTreasury];
    // Use oracle to check if there is actually enough gold
    // in custody to validate this operation
    uint256 lockedGrams =  LockedGoldOracle(_oracle).lockedGold();
    // Should reject mint if it would make the total supply
    // exceed the amount actually locked in vault
    require(lockedGrams >= totalCirculation().add(value),
            "Insufficent grams locked in LockedGoldOracle to complete operation");
    // If we have sufficient balance, just move from the unbacked to backed
    // treasury address
    if (value <= unbackedBalance) {
      _transfer(_unbackedTreasury, _backedTreasury, value);
    } else {
      if (unbackedBalance > 0) {
        // There is no sufficient balance, so we have to both transfer and mint new tokens
        // Transfer the remaining unbacked treasury balance to backed treasury
        _transfer(_unbackedTreasury, _backedTreasury, unbackedBalance);
      }
      // And mint the remaining to the backed treasury
      _mint(value.sub(unbackedBalance));
    }
    emit AddBackedGold(value);
    return true;
  }
  /**
  * @dev Manually pay storage fees on senders address. Exchanges may want to
  * periodically call this function to pay owed storage fees. This is a
  * cheaper option than 'send to self', which would also trigger paying
  * storage fees
  *
  * @return A boolean that indicates if the operation was successful.
  */
  function payStorageFee() external returns (bool) {
    _updateActivity(msg.sender);
    _payStorageFee(msg.sender);
    return true;
  }
  function setAccountInactive(address account) external onlyEnforcer returns (bool) {
    require(_shouldMarkInactive(account), "Account not eligible to be marked inactive");
    _setInactive(account);
  }
  /**
  * @dev Contract allows the forcible collection of storage fees on an address
  * if it is has been more than than 365 days since the last time storage fees
  * were paid on this address.
  *
  * Alternatively inactive fees may also be collected periodically on a prorated
  * basis if the account is currently marked as inactive.
  *
  * @param account The address to pay storage fees on
  * @return A boolean that indicates if the operation was successful.
  */
  function forcePayFees(address account) external onlyEnforcer returns(bool) {
    require(account != address(0));
    require(_balances[account] > 0,
            "Account has no balance, cannot force paying fees");
    // If account is inactive, pay inactive fees
    if (isInactive(account)) {
      uint256 paid = _payInactiveFee(account);
      require(paid > 0);
    } else if (_shouldMarkInactive(account)) {
      // If it meets inactive threshold, but hasn't been set yet, set it.
      // This will also trigger automatic payment of owed storage fees
      // before starting inactive fees
      _setInactive(account);
    } else {
      // Otherwise just force paying owed storage fees, which can only
      // be called if they are more than 365 days overdue
      require(daysSincePaidStorageFee(account) >= YEAR,
              "Account has paid storage fees more recently than 365 days");
      uint256 paid = _payStorageFee(account);
      require(paid > 0, "No appreciable storage fees due, will refund gas");
    }
  }
  /**
  * @dev Set the address that can force collecting fees from users
  * @param enforcer The address to force collecting fees
  * @return An bool representing successfully changing enforcer address
  */
  function setFeeEnforcer(address enforcer) external onlyOwner returns(bool) {
    require(enforcer != address(0));
    _feeEnforcer = enforcer;
    setFeeExempt(_feeEnforcer);
    return true;
  }
  /**
  * @dev Set the address to collect fees
  * @param newFeeAddress The address to collect storage and transfer fees
  * @return An bool representing successfully changing fee address
  */
  function setFeeAddress(address newFeeAddress) external onlyOwner returns(bool) {
    require(newFeeAddress != address(0));
    require(newFeeAddress != _unbackedTreasury,
            "Cannot set fee address to unbacked treasury");
    _feeAddress = newFeeAddress;
    setFeeExempt(_feeAddress);
    return true;
  }
  /**
  * @dev Set the address to deposit tokens when redeeming for physical locked bars.
  * @param newRedeemAddress The address to redeem tokens for bars
  * @return An bool representing successfully changing redeem address
  */
  function setRedeemAddress(address newRedeemAddress) external onlyOwner returns(bool) {
    require(newRedeemAddress != address(0));
    require(newRedeemAddress != _unbackedTreasury,
            "Cannot set redeem address to unbacked treasury");
    _redeemAddress = newRedeemAddress;
    setFeeExempt(_redeemAddress);
    return true;
  }
  /**
  * @dev Set the address of backed treasury
  * @param newBackedAddress The address of backed treasury
  * @return An bool representing successfully changing backed address
  */
  function setBackedAddress(address newBackedAddress) external onlyOwner returns(bool) {
    require(newBackedAddress != address(0));
    require(newBackedAddress != _unbackedTreasury,
            "Cannot set backed address to unbacked treasury");
    _backedTreasury = newBackedAddress;
    setFeeExempt(_backedTreasury);
    return true;
  }
  /**
  * @dev Set the address to unbacked treasury
  * @param newUnbackedAddress The address of unbacked treasury
  * @return An bool representing successfully changing unbacked address
  */
  function setUnbackedAddress(address newUnbackedAddress) external onlyOwner returns(bool) {
    require(newUnbackedAddress != address(0));
    require(newUnbackedAddress != _backedTreasury,
            "Cannot set unbacked treasury to backed treasury");
    require(newUnbackedAddress != _feeAddress,
            "Cannot set unbacked treasury to fee address ");
    require(newUnbackedAddress != _redeemAddress,
            "Cannot set unbacked treasury to fee address ");
    _unbackedTreasury = newUnbackedAddress;
    setFeeExempt(_unbackedTreasury);
    return true;
  }
  /**
  * @dev Set the LockedGoldOracle address
  * @param oracleAddress The address for oracle
  * @return An bool representing successfully changing oracle address
  */
  function setOracleAddress(address oracleAddress) external onlyOwner returns(bool) {
    require(oracleAddress != address(0));
    _oracle = oracleAddress;
    return true;
  }
  /**
  * @dev Set the number of days before storage fees begin accruing.
  * @param daysGracePeriod The global setting for the grace period before storage
  * fees begin accruing. Note that calling this will not change the grace period
  * for addresses already actively inside a grace period
  */
  function setStorageFeeGracePeriodDays(uint256 daysGracePeriod) external onlyOwner {
    _storageFeeGracePeriodDays = daysGracePeriod;
  }
  /**
  * @dev Set this account as being exempt from transfer fees. This may be used
  * in special circumstance for cold storage addresses owed by Cache, exchanges, etc.
  * @param account The account to exempt from transfer fees
  */
  function setTransferFeeExempt(address account) external onlyOwner {
    _transferFeeExempt[account] = true;
  }
  /**
  * @dev Set this account as being exempt from storage fees. This may be used
  * in special circumstance for cold storage addresses owed by Cache, exchanges, etc.
  * @param account The account to exempt from storage fees
  */
  function setStorageFeeExempt(address account) external onlyOwner {
    _storageFeeExempt[account] = true;
  }
  /**
  * @dev Set account is no longer exempt from all fees
  * @param account The account to reactivate fees
  */
  function unsetFeeExempt(address account) external onlyOwner {
    _transferFeeExempt[account] = false;
    _storageFeeExempt[account] = false;
  }
  /**
  * @dev Set a new transfer fee in basis points, must be less than or equal to 10 basis points
  * @param fee The new transfer fee in basis points
  */
  function setTransferFeeBasisPoints(uint256 fee) external onlyOwner {
    require(fee <= MAX_TRANSFER_FEE_BASIS_POINTS,
            "Transfer fee basis points must be an integer between 0 and 10");
    _transferFeeBasisPoints = fee;
  }
  /**
  * @dev Gets the balance of the specified address deducting owed fees and
  * accounting for the maximum amount that could be sent including transfer fee
  * @param owner The address to query the balance of.
  * @return An uint256 representing the amount sendable by the passed address
  * including transaction and storage fees
  */
  function balanceOf(address owner) external view returns (uint256) {
    return calcSendAllBalance(owner);
  }
  /**
  * @dev Gets the balance of the specified address not deducting owed fees.
  * this returns the 'traditional' ERC-20 balance that represents the balance
  * currently stored in contract storage.
  * @param owner The address to query the balance of.
  * @return An uint256 representing the amount stored in passed address
  */
  function balanceOfNoFees(address owner) external view returns (uint256) {
    return _balances[owner];
  }
  /**
  * @dev Total number of tokens in existence. This includes tokens
  * in the unbacked treasury that are essentially unusable and not
  * in circulation
  * @return A uint256 representing the total number of minted tokens
  */
  function totalSupply() external view returns (uint256) {
    return _totalSupply;
  }
  /**
  * @dev Function to check the amount of tokens that an owner allowed to a spender.
  * @param owner address The address which owns the funds.
  * @param spender address The address which will spend the funds.
  * @return A uint256 specifying the amount of tokens still available for the spender.
  */
  function allowance(address owner, address spender) external view returns (uint256) {
    return _allowances[owner][spender];
  }
  /**
  * @return address that can force paying overdue inactive fees
  */
  function feeEnforcer() external view returns(address) {
    return _feeEnforcer;
  }
  /**
   * @return address where fees are collected
   */
  function feeAddress() external view returns(address) {
    return _feeAddress;
  }
  /**
   * @return address for redeeming tokens for gold bars
   */
  function redeemAddress() external view returns(address) {
    return _redeemAddress;
  }
  /**
   * @return address for backed treasury
   */
  function backedTreasury() external view returns(address) {
    return _backedTreasury;
  }
  /**
  * @return address for unbacked treasury
  */
  function unbackedTreasury() external view returns(address) {
    return _unbackedTreasury;
  }
  /**
  * @return address for oracle contract
  */
  function oracleAddress() external view returns(address) {
    return _oracle;
  }
  /**
  * @return the current number of days and address is exempt
  * from storage fees upon receiving tokens
  */
  function storageFeeGracePeriodDays() external view returns(uint256) {
    return _storageFeeGracePeriodDays;
  }
  /**
  * @return the current transfer fee in basis points [0-10]
  */
  function transferFeeBasisPoints() external view returns(uint256) {
    return _transferFeeBasisPoints;
  }
  /**
  * @dev Simulate the transfer from one address to another see final balances and associated fees
  * @param from The address to transfer from.
  * @param to The address to transfer to.
  * @param value The amount to be transferred.
  * @return See _simulateTransfer function
  */
  function simulateTransfer(address from, address to, uint256 value) external view returns (uint256[5] memory) {
    return _simulateTransfer(from, to, value);
  }
  /**
  * @dev Set this account as being exempt from all fees. This may be used
  * in special circumstance for cold storage addresses owed by Cache, exchanges, etc.
  * @param account The account to exempt from storage and transfer fees
  */
  function setFeeExempt(address account) public onlyOwner {
    _transferFeeExempt[account] = true;
    _storageFeeExempt[account] = true;
  }
  /**
  * @dev Check if the address given is extempt from storage fees
  * @param account The address to check
  * @return A boolean if the address passed is exempt from storage fees
  */
  function isStorageFeeExempt(address account) public view returns(bool) {
    return _storageFeeExempt[account];
  }
  /**
  * @dev Check if the address given is extempt from transfer fees
  * @param account The address to check
  * @return A boolean if the address passed is exempt from transfer fees
  */
  function isTransferFeeExempt(address account) public view returns(bool) {
    return _transferFeeExempt[account];
  }
  /**
  * @dev Check if the address given is extempt from transfer fees
  * @param account The address to check
  * @return A boolean if the address passed is exempt from transfer fees
  */
  function isAllFeeExempt(address account) public view returns(bool) {
    return _transferFeeExempt[account] && _storageFeeExempt[account];
  }
  /**
  * @dev Check if the address is considered inactive for not having transacted with
  * the contract for INACTIVE_THRESHOLD_DAYS
  * @param account The address to check
  * @return A boolean if the address passed is considered inactive
  */
  function isInactive(address account) public view returns(bool) {
    return _inactiveFeePerYear[account] > 0;
  }
  /**
  * @dev Total number of tokens that are actually in circulation, which is
  * total tokens excluding the unbacked treasury
  * @return A uint256 representing the total number of tokens in circulation
  */
  function totalCirculation() public view returns (uint256) {
    return _totalSupply.sub(_balances[_unbackedTreasury]);
  }
  /**
  * @dev Get the number of days since the account last paid storage fees
  * @param account The address to check
  * @return A uint256 representing the number of days since storage fees where last paid
  */
  function daysSincePaidStorageFee(address account) public view returns(uint256) {
    if (isInactive(account) || _timeStorageFeePaid[account] == 0) {
      return 0;
    }
    return block.timestamp.sub(_timeStorageFeePaid[account]).div(DAY);
  }
  /**
  * @dev Get the days since the account last sent a transaction to the contract (activity)
  * @param account The address to check
  * @return A uint256 representing the number of days since the address last had activity
  * with the contract
  */
  function daysSinceActivity(address account) public view returns(uint256) {
    if (_timeLastActivity[account] == 0) {
      return 0;
    }
    return block.timestamp.sub(_timeLastActivity[account]).div(DAY);
  }
  /**
  * @dev Returns the total number of fees owed on a particular address
  * @param account The address to check
  * @return The total storage and inactive fees owed on the address
  */
  function calcOwedFees(address account) public view returns(uint256) {
    return calcStorageFee(account).add(calcInactiveFee(account));
  }
  /**
   * @dev Calculate the current storage fee owed for a given address
   * @param account The address to check
   * @return A uint256 representing current storage fees for the address
   */
  function calcStorageFee(address account) public view returns(uint256) {
    // If an account is in an inactive state those fees take over and
    // storage fees are effectively paused
    uint256 balance = _balances[account];
    if (isInactive(account) || isStorageFeeExempt(account) || balance == 0) {
      return 0;
    }
    uint256 daysSinceStoragePaid = daysSincePaidStorageFee(account);
    uint256 daysInactive = daysSinceActivity(account);
    uint256 gracePeriod = _storageFeeGracePeriod[account];
    // If there is a grace period, we can deduct it from the daysSinceStoragePaid
    if (gracePeriod > 0) {
      if (daysSinceStoragePaid > gracePeriod) {
        daysSinceStoragePaid = daysSinceStoragePaid.sub(gracePeriod);
      } else {
        daysSinceStoragePaid = 0;
      }
    }
    if (daysSinceStoragePaid == 0) {
      return 0;
    }
    // This is an edge case where the account has not yet been marked inactive, but
    // will be marked inactive whenever there is a transaction allowing it to be marked.
    // Therefore we know storage fees will only be valid up to a point, and inactive
    // fees will take over.
    if (daysInactive >= INACTIVE_THRESHOLD_DAYS) {
      // This should not be at risk of being negative, because its impossible to force paying
      // storage fees without also setting the account to inactive, so if we are here it means
      // the last time storage fees were paid was BEFORE the account became eligible to be inactive
      // and it's always the case that daysSinceStoragePaid > daysInactive.sub(INACTIVE_THRESHOLD_DAYS)
      daysSinceStoragePaid = daysSinceStoragePaid.sub(daysInactive.sub(INACTIVE_THRESHOLD_DAYS));
    }
    // The normal case with normal storage fees
    return storageFee(balance, daysSinceStoragePaid);
  }
  /**
   * @dev Calculate the current inactive fee for a given address
   * @param account The address to check
   * @return A uint256 representing current inactive fees for the address
   */
  function calcInactiveFee(address account) public view returns(uint256) {
    uint256 balance = _balances[account];
    uint256 daysInactive = daysSinceActivity(account);
    // if the account is marked inactive already, can use the snapshot balance
    if (isInactive(account)) {
      return _calcInactiveFee(balance,
                          daysInactive,
                          _inactiveFeePerYear[account],
                          _inactiveFeePaid[account]);
    } else if (_shouldMarkInactive(account)) {
      // Account has not yet been marked inactive in contract, but the inactive fees will still be due.
      // Just assume snapshotBalance will be current balance after fees
      uint256 snapshotBalance = balance.sub(calcStorageFee(account));
      return _calcInactiveFee(snapshotBalance,                          // current balance
                              daysInactive,                             // number of days inactive
                              _calcInactiveFeePerYear(snapshotBalance), // the inactive fee per year based on balance
                              0);                                       // fees paid already
    }
    return 0;
  }
  /**
   * @dev Calculate the amount that would clear the balance from the address
   * accounting for owed storage and transfer fees
   * accounting for storage and transfer fees
   * @param account The address to check
   * @return A uint256 representing total amount an address has available to send
   */
  function calcSendAllBalance(address account) public view returns (uint256) {
    require(account != address(0));
    // Internal addresses pay no fees, so they can send their entire balance
    uint256 balanceAfterStorage = _balances[account].sub(calcOwedFees(account));
    if (_transferFeeBasisPoints == 0 || isTransferFeeExempt(account)) {
      return balanceAfterStorage;
    }
    // Edge cases where remaining balance is 0.00000001, but is effectively 0
    if (balanceAfterStorage <= 1) {
      return 0;
    }
    // Calculate the send all amount including storage fee
    // Send All = Balance / 1.001
    // and round up 0.00000001
    uint256 divisor = TOKEN.add(_transferFeeBasisPoints.mul(BASIS_POINTS_MULTIPLIER));
    uint256 sendAllAmount = balanceAfterStorage.mul(TOKEN).div(divisor).add(1);
    // Calc transfer fee on send all amount
    uint256 transFee = sendAllAmount.mul(_transferFeeBasisPoints).div(BASIS_POINTS_MULTIPLIER);
    // Fix to include rounding errors
    if (sendAllAmount.add(transFee) > balanceAfterStorage) {
      return sendAllAmount.sub(1);
    }
    return sendAllAmount;
  }
  /*
   * @dev Calculate the transfer fee on an amount
   * @param value The value being sent
   * @return A uint256 representing the transfer fee on sending the value given
   */
  function calcTransferFee(address account, uint256 value) public view returns(uint256) {
    if (isTransferFeeExempt(account)) {
      return 0;
    }
    // Basis points -> decimal multiplier:
    // f(x) = x / 10,0000 (10 basis points is 0.001)
    // So transfer fee working with integers =
    // f(balance, basis) = (balance * TOKEN) / (10,000 * TOKEN / basis)
    return value.mul(_transferFeeBasisPoints).div(BASIS_POINTS_MULTIPLIER);
  }
  /*
   * @dev Calculate the storage fee for a given balance after a certain number of
   * days have passed since the last time fees were paid.
   * @param balance The current balance of the address
   * @param daysSinceStoragePaid The number days that have passed since fees where last paid
   * @return A uint256 representing the storage fee owed
   */
  function storageFee(uint256 balance, uint256 daysSinceStoragePaid) public pure returns(uint256) {
    uint256 fee = balance.mul(TOKEN).mul(daysSinceStoragePaid).div(YEAR).div(STORAGE_FEE_DENOMINATOR);
    if (fee > balance) {
      return balance;
    }
    return fee;
  }
  /**
   * @dev Approve an address to spend another addresses' tokens.
   * @param owner The address that owns the tokens.
   * @param spender The address that will spend the tokens.
   * @param value The number of tokens that can be spent.
   */
  function _approve(address owner, address spender, uint256 value) internal {
    require(spender != address(0));
    require(owner != address(0));
    _allowances[owner][spender] = value;
    emit Approval(owner, spender, value);
  }
  /**
  * @dev Transfer token for a specified addresses. Transfer is modified from a
  * standard ERC20 contract in that it must also process transfer and storage fees
  * for the token itself. Additionally there are certain internal addresses that
  * are not subject to fees.
  * @param from The address to transfer from.
  * @param to The address to transfer to.
  * @param value The amount to be transferred.
  */
  function _transfer(address from, address to, uint256 value) internal {
    _transferRestrictions(to, from);
    // If the account was previously inactive and initiated the transfer, the
    // inactive fees and storage fees have already been paid by the time we get here
    // via the _updateActivity() call
    uint256 storageFeeFrom = calcStorageFee(from);
    uint256 storageFeeTo = 0;
    uint256 allFeeFrom = storageFeeFrom;
    uint256 balanceFromBefore = _balances[from];
    uint256 balanceToBefore = _balances[to];
    // If not sending to self can pay storage and transfer fee
    if (from != to) {
      // Need transfer fee and storage fee for receiver if not sending to self
      allFeeFrom = allFeeFrom.add(calcTransferFee(from, value));
      storageFeeTo = calcStorageFee(to);
      _balances[from] = balanceFromBefore.sub(value).sub(allFeeFrom);
      _balances[to] = balanceToBefore.add(value).sub(storageFeeTo);
      _balances[_feeAddress] = _balances[_feeAddress].add(allFeeFrom).add(storageFeeTo);
    } else {
      // Only storage fee if sending to self
      _balances[from] = balanceFromBefore.sub(storageFeeFrom);
      _balances[_feeAddress] = _balances[_feeAddress].add(storageFeeFrom);
    }
    // Regular Transfer
    emit Transfer(from, to, value);
    // Fee transfer on `from` address
    if (allFeeFrom > 0) {
      emit Transfer(from, _feeAddress, allFeeFrom);
      if (storageFeeFrom > 0) {
        _timeStorageFeePaid[from] = block.timestamp;
        _endGracePeriod(from);
      }
    }
    // If first time receiving coins, set the grace period
    // and start the the activity clock and storage fee clock
    if (_timeStorageFeePaid[to] == 0) {
      // We may change the grace period in the future so we want to
      // preserve it per address so there is no retroactive deduction
      _storageFeeGracePeriod[to] = _storageFeeGracePeriodDays;
      _timeLastActivity[to] = block.timestamp;
      _timeStorageFeePaid[to] = block.timestamp;
    }
    // Fee transfer on `to` address
    if (storageFeeTo > 0) {
      emit Transfer(to, _feeAddress, storageFeeTo);
      _timeStorageFeePaid[to] = block.timestamp;
      _endGracePeriod(to);
    } else if (balanceToBefore < MIN_BALANCE_FOR_FEES) {
      // MIN_BALANCE_FOR_FEES is the minimum amount in which a storage fee
      // would be due after a sigle day, so if the balance is above that,
      // the storage fee would always be greater than 0.
      //
      // This avoids the following condition:
      // 1. User receives tokens
      // 2. Users sends all but a tiny amount to another address
      // 3. A year later, the user receives more tokens. Because
      // their previous balance was super small, there were no appreciable
      // storage fee, therefore the storage fee clock was not reset
      // 4. User now owes storage fees on entire balance, as if they
      // held tokens for 1 year, instead of resetting the clock to now.
      _timeStorageFeePaid[to] = block.timestamp;
    }
    // If transferring to unbacked treasury, tokens are being taken from
    // circulation, because gold is being 'unlocked' from the vault
    if (to == _unbackedTreasury) {
      emit RemoveGold(value);
    }
  }
  /**
  * @dev Function to mint tokens to backed treasury. In general this method
  * will not be called on it's own, but instead will be called from
  * addBackedTokens.
  * @param value The amount of tokens to mint to backed treasury
  * @return A boolean that indicates if the operation was successful.
  */
  function _mint(uint256 value) internal returns(bool) {
    // Can't break supply cap
    require(_totalSupply.add(value) <= SUPPLY_CAP, "Call would exceed supply cap");
    // Can only mint if the unbacked treasury balance is 0
    require(_balances[_unbackedTreasury] == 0, "The unbacked treasury balance is not 0");
    // Can only mint to the backed treasury
    _totalSupply = _totalSupply.add(value);
    _balances[_backedTreasury] = _balances[_backedTreasury].add(value);
    emit Transfer(address(0), _backedTreasury, value);
    return true;
  }
  /**
   * @dev Apply storage fee deduction
   * @param account The account to pay storage fees
   * @return A uint256 representing the storage fee paid
   */
  function _payStorageFee(address account) internal returns(uint256) {
    uint256 storeFee = calcStorageFee(account);
    if (storeFee == 0) {
      return 0;
    }
    // Reduce account balance and add to fee address
    _balances[account] = _balances[account].sub(storeFee);
    _balances[_feeAddress] = _balances[_feeAddress].add(storeFee);
    emit Transfer(account, _feeAddress, storeFee);
    _timeStorageFeePaid[account] = block.timestamp;
    _endGracePeriod(account);
    return storeFee;
  }
  /**
   * @dev Apply inactive fee deduction
   * @param account The account to pay inactive fees
   * @return A uint256 representing the inactive fee paid
   */
  function _payInactiveFee(address account) internal returns(uint256) {
    uint256 fee = _calcInactiveFee(
        _balances[account],
        daysSinceActivity(account),
        _inactiveFeePerYear[account],
        _inactiveFeePaid[account]);
    if (fee == 0) {
      return 0;
    }
    _balances[account] = _balances[account].sub(fee);
    _balances[_feeAddress] = _balances[_feeAddress].add(fee);
    _inactiveFeePaid[account] = _inactiveFeePaid[account].add(fee);
    emit Transfer(account, _feeAddress, fee);
    return fee;
  }
  function _shouldMarkInactive(address account) internal view returns(bool) {
    // Can only mark an account as inactive if
    //
    // 1. it's not fee exempt
    // 2. it has a balance
    // 3. it's been over INACTIVE_THRESHOLD_DAYS since last activity
    // 4. it's not already marked inactive
    // 5. the storage fees owed already consume entire balance
    if (account != address(0) &&
        _balances[account] > 0 &&
        daysSinceActivity(account) >= INACTIVE_THRESHOLD_DAYS &&
        !isInactive(account) &&
        !isAllFeeExempt(account) &&
        _balances[account].sub(calcStorageFee(account)) > 0) {
      return true;
    }
    return false;
  }
  /**
  * @dev Mark an account as inactive. The function will automatically deduct
  * owed storage fees and inactive fees in one go.
  *
  * @param account The account to mark inactive
  */
  function _setInactive(address account) internal {
    // First get owed storage fees
    uint256 storeFee = calcStorageFee(account);
    uint256 snapshotBalance = _balances[account].sub(storeFee);
    // all _setInactive calls are wrapped in _shouldMarkInactive, which
    // already checks this, so we shouldn't hit this condition
    assert(snapshotBalance > 0);
    // Set the account inactive on deducted balance
    _inactiveFeePerYear[account] = _calcInactiveFeePerYear(snapshotBalance);
    emit AccountInactive(account, _inactiveFeePerYear[account]);
    uint256 inactiveFees = _calcInactiveFee(snapshotBalance,
                                            daysSinceActivity(account),
                                            _inactiveFeePerYear[account],
                                            0);
    // Deduct owed storage and inactive fees
    uint256 fees = storeFee.add(inactiveFees);
    _balances[account] = _balances[account].sub(fees);
    _balances[_feeAddress] = _balances[_feeAddress].add(fees);
    _inactiveFeePaid[account] = _inactiveFeePaid[account].add(inactiveFees);
    emit Transfer(account, _feeAddress, fees);
    // Reset storage fee clock if storage fees paid
    if (storeFee > 0) {
      _timeStorageFeePaid[account] = block.timestamp;
      _endGracePeriod(account);
    }
  }
  /**
  * @dev Update the activity clock on an account thats originated a transaction.
  * If the account has previously been marked inactive or should have been
  * marked inactive, it will opportunistically collect those owed fees.
  *
  * @param account The account to update activity
  */
  function _updateActivity(address account) internal {
    // Cache has the ability to force collecting storage and inactivity fees,
    // but in the event an address was missed, can we still detect if the
    // account was inactive when they next transact
    //
    // Here we simply set the account as being inactive, collect the previous
    // storage and inactive fees that were owed, and then reactivate the account
    if (_shouldMarkInactive(account)) {
      // Call will pay existing storage fees before marking inactive
      _setInactive(account);
    }
    // Pay remaining fees and reset fee clocks
    if (isInactive(account)) {
      _payInactiveFee(account);
      _inactiveFeePerYear[account] = 0;
      _timeStorageFeePaid[account] = block.timestamp;
      emit AccountReActive(account);
    }
    // The normal case will just hit this and update
    // the activity clock for the account
    _timeLastActivity[account] = block.timestamp;
  }
  /**
   * @dev Turn off storage fee grace period for an address the first
   * time storage fees are paid (after grace period has ended)
   * @param account The account to turn off storage fee grace period
   */
  function _endGracePeriod(address account) internal {
    if (_storageFeeGracePeriod[account] > 0) {
      _storageFeeGracePeriod[account] = 0;
    }
  }
  /**
  * @dev Enforce the rules of which addresses can transfer to others
  * @param to The sending address
  * @param from The receiving address
  */
  function _transferRestrictions(address to, address from) internal view {
    require(from != address(0));
    require(to != address(0));
    require(to != address(this), "Cannot transfer tokens to the contract");
    // unbacked treasury can only transfer to backed treasury
    if (from == _unbackedTreasury) {
      require(to == _backedTreasury,
              "Unbacked treasury can only transfer to backed treasury");
    }
    // redeem address can only transfer to unbacked or backed treasury
    if (from == _redeemAddress) {
      require((to == _unbackedTreasury) || (to == _backedTreasury),
              "Redeem address can only transfer to treasury");
    }
    // Only the backed treasury  and redeem address
    // can transfer to unbacked treasury
    if (to == _unbackedTreasury) {
      require((from == _backedTreasury) || (from == _redeemAddress),
              "Unbacked treasury can only receive from redeem address and backed treasury");
    }
    // Only the unbacked treasury can transfer to the backed treasury
    if (to == _backedTreasury) {
      require((from == _unbackedTreasury) || (from == _redeemAddress),
              "Only unbacked treasury and redeem address can transfer to backed treasury");
    }
  }
  /**
   * @dev Simulate the transfer from one address to another see final balances and associated fees
   * @param from address The address which you want to send tokens from
   * @param to address The address which you want to transfer to
   * @return a uint256 array of 5 values representing the
   * [0] storage fees `from`
   * [1] storage fees `to`
   * [2] transfer fee `from`
   * [3] final `from` balance
   * [4] final `to` balance
   */
  function _simulateTransfer(address from, address to, uint256 value) internal view returns (uint256[5] memory) {
    uint256[5] memory ret;
    // Return value slots
    // 0 - fees `from`
    // 1 - fees `to`
    // 2 - transfer fee `from`
    // 3 - final `from` balance
    // 4 - final `to` balance
    ret[0] = calcOwedFees(from);
    ret[1] = 0;
    ret[2] = 0;
    // Don't double charge storage fee sending to self
    if (from != to) {
      ret[1] = calcOwedFees(to);
      ret[2] = calcTransferFee(from, value);
      ret[3] = _balances[from].sub(value).sub(ret[0]).sub(ret[2]);
      ret[4] = _balances[to].add(value).sub(ret[1]);
    } else {
      ret[3] = _balances[from].sub(ret[0]);
      ret[4] = ret[3];
    }
    return ret;
  }
  /**
  * @dev Calculate the amount of inactive fees due per year on the snapshot balance.
  * Should return 50 basis points or 1 token minimum.
  *
  * @param snapshotBalance The balance of the account when marked inactive
  * @return uint256 the inactive fees due each year
  */
  function _calcInactiveFeePerYear(uint256 snapshotBalance) internal pure returns(uint256) {
    uint256 inactiveFeePerYear = snapshotBalance.mul(TOKEN).div(INACTIVE_FEE_DENOMINATOR);
    if (inactiveFeePerYear < TOKEN) {
      return TOKEN;
    }
    return inactiveFeePerYear;
  }
  /**
  * @dev Calcuate inactive fees due on an account
  * @param balance The current account balance
  * @param daysInactive The number of days the account has been inactive
  * @param feePerYear The inactive fee per year based on snapshot balance
  * @param paidAlready The amount of inactive fees that have been paid already
  * @return uint256 for inactive fees due
  */
  function _calcInactiveFee(uint256 balance,
                        uint256 daysInactive,
                        uint256 feePerYear,
                        uint256 paidAlready) internal pure returns(uint256) {
    uint256 daysDue = daysInactive.sub(INACTIVE_THRESHOLD_DAYS);
    uint256 totalDue = feePerYear.mul(TOKEN).mul(daysDue).div(YEAR).div(TOKEN).sub(paidAlready);
    // The fee per year can be off by 0.00000001 so we can collect
    // the final dust after 200 years
    if (totalDue > balance || balance.sub(totalDue) <= 200) {
      return balance;
    }
    return totalDue;
  }
}
设置
{
  "compilationTarget": {
    "CacheGold.sol": "CacheGold"
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"unbackedTreasury","type":"address"},{"internalType":"address","name":"backedTreasury","type":"address"},{"internalType":"address","name":"feeAddress","type":"address"},{"internalType":"address","name":"redeemAddress","type":"address"},{"internalType":"address","name":"oracle","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"feePerYear","type":"uint256"}],"name":"AccountInactive","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"AccountReActive","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AddBackedGold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RemoveGold","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"constant":true,"inputs":[],"name":"INACTIVE_THRESHOLD_DAYS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"SUPPLY_CAP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"addBackedTokens","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"backedTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOfNoFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"calcInactiveFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"calcOwedFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"calcSendAllBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"calcStorageFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"calcTransferFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"daysSinceActivity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"daysSincePaidStorageFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"feeAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeEnforcer","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"forcePayFees","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isAllFeeExempt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isInactive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isStorageFeeExempt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"isTransferFeeExempt","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"oracleAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"payStorageFee","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"redeemAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setAccountInactive","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newBackedAddress","type":"address"}],"name":"setBackedAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newFeeAddress","type":"address"}],"name":"setFeeAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"enforcer","type":"address"}],"name":"setFeeEnforcer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setFeeExempt","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"oracleAddress","type":"address"}],"name":"setOracleAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newRedeemAddress","type":"address"}],"name":"setRedeemAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setStorageFeeExempt","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"daysGracePeriod","type":"uint256"}],"name":"setStorageFeeGracePeriodDays","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"name":"setTransferFeeBasisPoints","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setTransferFeeExempt","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newUnbackedAddress","type":"address"}],"name":"setUnbackedAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"simulateTransfer","outputs":[{"internalType":"uint256[5]","name":"","type":"uint256[5]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"daysSinceStoragePaid","type":"uint256"}],"name":"storageFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"storageFeeGracePeriodDays","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalCirculation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"transferFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"unbackedTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"unsetFeeExempt","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}]