// File: Erc2612.sol



// SPDX-License-Identifier: GPL-3.0-or-later
// Adapted from OpenZeppelin ERC2612 (ERC20Permit)

pragma solidity >= 0.8.4;

import './Hash.sol';
import './PErc20.sol';
import './IErc2612.sol';

/**
* @dev Extension of {ERC20} that allows token holders to use their tokens
* without sending any transactions by setting {IERC20-allowance} with a
* signature using the {permit} method, and then spend them via
* {IERC20-transferFrom}.
* NOTE: Naming convention is kept OZStyle vs our own OzStyle to prevent clashing
*
* The {permit} signature mechanism conforms to the {IERC2612} interface.
*/
contract Erc2612 is PErc20, IErc2612 {
  mapping (address => uint256) public override nonces;

  bytes32 public immutable domain;

  /// @param n name for the token
  /// @param s symbol for the token
  /// @param d decimals for the token
  constructor(string memory n, string memory s, uint8 d) PErc20(n, s, d) {
    domain = Hash.domain(n, '1', block.chainid, address(this));
  }

  /**
  * @dev See {IERC2612-permit}.
  *
  * In cases where the free option is not a concern, deadline can simply be
  * set to uint(-1), so it should be seen as an optional parameter
  *
  * @param o Address of the owner
  * @param spender Address of the spender
  * @param a Amount to be approved
  * @param d Deadline at which the permission is no longer valid
  * NOTE: Last three args (v, r, s) are the components of a valid ECDSA signature
  */
  function permit(address o, address spender, uint256 a, uint256 d, uint8 v, bytes32 r, bytes32 s) public virtual override {
    require(d >= block.timestamp, 'erc2612 expired deadline');

    bytes32 hashStruct = Hash.permit(o, spender, a, nonces[o]++, d);
    bytes32 hash = Hash.message(domain, hashStruct); 
    address signer = ecrecover(hash, v, r, s);

    require(signer != address(0) && signer == o, 'erc2612 invalid signature');
    _approve(o, spender, a);
  }
}

// File: Hash.sol



// SPDX-License-Identifier: UNLICENSED

pragma solidity >= 0.8.4;

/**
  @notice Encapsulation of the logic to produce EIP712 hashed domain and messages.
  Also to produce / verify hashed and signed Permits.
*/

library Hash {
  // EIP712 Domain Separator typeHash
  // keccak256(abi.encodePacked(
  //     'EIP712Domain(',
  //     'string name,',
  //     'string version,',
  //     'uint256 chainId,',
  //     'address verifyingContract',
  //     ')'
  // ));
  bytes32 constant internal DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

  // EIP2612 typeHash of a Permit
  // keccak256(abi.encodePacked(
  //     'Permit(',
  //     'address owner,',
  //     'address spender,',
  //     'uint256 value,',
  //     'uint256 nonce,',
  //     'uint256 deadline,',
  //     ')'
  // ));
  bytes32 constant internal PERMIT_TYPEHASH = 0x80772249b4aef1688b30651778f4249b05cb73b517d98482439b9d8999b30602;

  /// @param n EIP712 domain name
  /// @param version EIP712 semantic version string
  /// @param i Chain ID
  /// @param verifier address of the verifying contract
  function domain(string memory n, string memory version, uint256 i, address verifier) internal pure returns (bytes32) {
    bytes32 hash;

    assembly {
      let nameHash := keccak256(add(n, 32), mload(n))
      let versionHash := keccak256(add(version, 32), mload(version))
      let pointer := mload(64)
      mstore(pointer, DOMAIN_TYPEHASH)
      mstore(add(pointer, 32), nameHash)
      mstore(add(pointer, 64), versionHash)
      mstore(add(pointer, 96), i)
      mstore(add(pointer, 128), verifier)
      hash := keccak256(pointer, 160)
    }

    return hash;
  }

  /// @param d Type hash of the domain separator (see Hash.domain)
  /// @param h EIP712 hash struct (Permit for example)
  function message(bytes32 d, bytes32 h) internal pure returns (bytes32) {
    bytes32 hash;

    assembly {
      let pointer := mload(64)
      mstore(pointer, 0x1901000000000000000000000000000000000000000000000000000000000000)
      mstore(add(pointer, 2), d)
      mstore(add(pointer, 34), h)
      hash := keccak256(pointer, 66)
    }

    return hash;
  }

  /// @param o Address of the owner
  /// @param s Address of the spender
  /// @param a Amount to be approved
  /// @param n Current nonce
  /// @param d Deadline at which the permission is no longer valid
  function permit(address o, address s, uint256 a, uint256 n, uint256 d) internal pure returns (bytes32) {
    return keccak256(abi.encode(PERMIT_TYPEHASH, o, s, a, n, d));
  }
}

// File: IErc2612.sol



// SPDX-License-Identifier: GPL-3.0-or-later
// Code adapted from OpenZeppelin IERCPermit
pragma solidity >= 0.8.4;

/**
 * @dev Interface of the ERC2612 standard as defined in the EIP.
 *
 * Adds the {permit} method, which can be used to change one's
 * {IERC20-allowance} without having to send a transaction, by signing a
 * message. This allows users to spend tokens without having to hold Ether.
 *
 * See https://eips.ethereum.org/EIPS/eip-2612.
 */
interface IErc2612 {
    /**
     * @dev Sets `amount` as the allowance of `spender` over `owner`'s tokens,
     * given `owner`'s signed approval.
     * @param o The owner
     * @param spender The spender
     * @param a The amount
     * @param d The deadline
     * @param v v portion of the ECDSA
     * @param r r portion of the ECDSA
     * @param s s portion of the ECDSA
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(address o, address spender, uint256 a, uint256 d, uint8 v, bytes32 r, bytes32 s) external;

    /**
     * @dev Returns the current ERC2612 nonce for `owner`. This value must be
     * @param o The owner
     *
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address o) external view returns (uint256);
}

// File: Interfaces.sol



// SPDX-License-Identifier: UNLICENSED

pragma solidity >= 0.8.4;

interface Erc20 {
  function decimals() external returns (uint8);
}

interface CErc20 {
	function exchangeRateCurrent() external returns (uint256);
  function underlying() external returns (address);
}

// File: IPErc20.sol



// SPDX-License-Identifier: MIT

pragma solidity >= 0.8.4;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IPErc20 {
    /**
     * @dev Returns the amount of tokens owned by `account`.
     * @param a Adress to fetch balance of
     */
    function balanceOf(address a) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     * @param r The recipient
     * @param a The amount transferred
     *
     * Emits a {Transfer} event.
     */
    function transfer(address r, uint256 a) 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.
     * @param o The owner
     * @param s The spender
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address o, address s) 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.
     *
     * IMPORTANT: 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 s The spender
     * @param a The amount to approve
     *
     * Emits an {Approval} event.
     */
    function approve(address s, uint256 a) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     * @param s The sender
     * @param r The recipient
     * @param a The amount to transfer
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address s, address r, uint256 a) 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: IZcToken.sol



// SPDX-License-Identifier: MIT

pragma solidity >= 0.8.4;

import "./IPErc20.sol";

/**
 * @dev Mint and burn interface for the ZCToken
 *
 */
interface IZcToken is IPErc20 {
    /**
     * @dev Mints...
     */
    function mint(address, uint256) external returns(bool);

    /**
     * @dev Burns...
     */
    function burn(address, uint256) external returns(bool);
}

// File: MarketPlace.sol



// SPDX-License-Identifier: UNLICENSED

pragma solidity >= 0.8.4;

import './Interfaces.sol';
import './ZcToken.sol';
import './VaultTracker.sol';

contract MarketPlace {
  struct Market {
    address cTokenAddr;
    address zcTokenAddr;
    address vaultAddr;
    uint256 maturityRate;
  }

  mapping (address => mapping (uint256 => Market)) public markets;

  address public admin;
  address public swivel;
  bool public paused;

  event Create(address indexed underlying, uint256 indexed maturity, address cToken, address zcToken, address vaultTracker);
  event Mature(address indexed underlying, uint256 indexed maturity, uint256 maturityRate, uint256 matured);
  event RedeemZcToken(address indexed underlying, uint256 indexed maturity, address indexed sender, uint256 amount);
  event RedeemVaultInterest(address indexed underlying, uint256 indexed maturity, address indexed sender);
  event CustodialInitiate(address indexed underlying, uint256 indexed maturity, address zcTarget, address nTarget, uint256 amount);
  event CustodialExit(address indexed underlying, uint256 indexed maturity, address zcTarget, address nTarget, uint256 amount);
  event P2pZcTokenExchange(address indexed underlying, uint256 indexed maturity, address from, address to, uint256 amount);
  event P2pVaultExchange(address indexed underlying, uint256 indexed maturity, address from, address to, uint256 amount);
  event TransferVaultNotional(address indexed underlying, uint256 indexed maturity, address from, address to, uint256 amount);

  constructor() {
    admin = msg.sender;
  }

  /// @param s Address of the deployed swivel contract
  /// @notice We only allow this to be set once
  function setSwivelAddress(address s) external authorized(admin) returns (bool) {
    require(swivel == address(0), 'swivel contract address already set');
    swivel = s;
    return true;
  }

  /// @param a Address of a new admin
  function transferAdmin(address a) external authorized(admin) returns (bool) {
    admin = a;
    return true;
  }

  /// @notice Allows the owner to create new markets
  /// @param m Maturity timestamp of the new market
  /// @param c cToken address associated with underlying for the new market
  /// @param n Name of the new zcToken market
  /// @param s Symbol of the new zcToken market
  function createMarket(
    uint256 m,
    address c,
    string memory n,
    string memory s
  ) external authorized(admin) unpaused() returns (bool) {
    address swivelAddr = swivel;
    require(swivelAddr != address(0), 'swivel contract address not set');

    address underAddr = CErc20(c).underlying();
    require(markets[underAddr][m].vaultAddr == address(0), 'market already exists');

    uint8 decimals = Erc20(underAddr).decimals();
    address zcTokenAddr = address(new ZcToken(underAddr, m, n, s, decimals));
    address vaultAddr = address(new VaultTracker(m, c, swivelAddr));
    markets[underAddr][m] = Market(c, zcTokenAddr, vaultAddr, 0);

    emit Create(underAddr, m, c, zcTokenAddr, vaultAddr);

    return true;
  }

  /// @notice Can be called after maturity, allowing all of the zcTokens to earn floating interest on Compound until they release their funds
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  function matureMarket(address u, uint256 m) public unpaused() returns (bool) {
    Market memory mkt = markets[u][m];

    require(mkt.maturityRate == 0, 'market already matured');
    require(block.timestamp >= m, "maturity not reached");

    // set the base maturity cToken exchange rate at maturity to the current cToken exchange rate
    uint256 currentExchangeRate = CErc20(mkt.cTokenAddr).exchangeRateCurrent();
    markets[u][m].maturityRate = currentExchangeRate;

    require(VaultTracker(mkt.vaultAddr).matureVault(currentExchangeRate), 'mature vault failed');

    emit Mature(u, m, currentExchangeRate, block.timestamp);

    return true;
  }

  /// @notice Allows Swivel caller to deposit their underlying, in the process splitting it - minting both zcTokens and vault notional.
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param t Address of the depositing user
  /// @param a Amount of notional being added
  function mintZcTokenAddingNotional(address u, uint256 m, address t, uint256 a) external authorized(swivel) unpaused() returns (bool) {
    Market memory mkt = markets[u][m];
    require(ZcToken(mkt.zcTokenAddr).mint(t, a), 'mint zcToken failed');
    require(VaultTracker(mkt.vaultAddr).addNotional(t, a), 'add notional failed');
    
    return true;
  }

  /// @notice Allows Swivel caller to deposit/burn both zcTokens + vault notional. This process is "combining" the two and redeeming underlying.
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param t Address of the combining/redeeming user
  /// @param a Amount of zcTokens being burned
  function burnZcTokenRemovingNotional(address u, uint256 m, address t, uint256 a) external authorized(swivel) unpaused() returns(bool) {
    Market memory mkt = markets[u][m];
    require(ZcToken(mkt.zcTokenAddr).burn(t, a), 'burn failed');
    require(VaultTracker(mkt.vaultAddr).removeNotional(t, a), 'remove notional failed');
    
    return true;
  }

  /// @notice Allows (via swivel) zcToken holders to redeem their tokens for underlying tokens after maturity has been reached.
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param t Address of the redeeming user
  /// @param a Amount of zcTokens being redeemed
  function redeemZcToken(address u, uint256 m, address t, uint256 a) external authorized(swivel) unpaused() returns (uint256) {
    Market memory mkt = markets[u][m];

    // if the market has not matured, mature it and redeem exactly the amount
    if (mkt.maturityRate == 0) {
      require(matureMarket(u, m), 'failed to mature the market');
    }

    // burn user's zcTokens
    require(ZcToken(mkt.zcTokenAddr).burn(t, a), 'could not burn');

    emit RedeemZcToken(u, m, t, a);

    if (mkt.maturityRate == 0) {
      return a;
    } else { 
      // if the market was already mature the return should include the amount + marginal floating interest generated on Compound since maturity
      return calculateReturn(u, m, a);
    }
  }

  /// @notice Allows Vault owners (via Swivel) to redeem any currently accrued interest
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param t Address of the redeeming user
  function redeemVaultInterest(address u, uint256 m, address t) external authorized(swivel) unpaused() returns (uint256) {
    // call to the floating market contract to release the position and calculate the interest generated
    uint256 interest = VaultTracker(markets[u][m].vaultAddr).redeemInterest(t);

    emit RedeemVaultInterest(u, m, t);

    return interest;
  }

  /// @notice Calculates the total amount of underlying returned including interest generated since the `matureMarket` function has been called
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param a Amount of zcTokens being redeemed
  function calculateReturn(address u, uint256 m, uint256 a) internal returns (uint256) {
    Market memory mkt = markets[u][m];
    uint256 rate = CErc20(mkt.cTokenAddr).exchangeRateCurrent();

    return (a * rate) / mkt.maturityRate;
  }

  /// @notice Return the ctoken address for a given market
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  function cTokenAddress(address u, uint256 m) external view returns (address) {
    return markets[u][m].cTokenAddr;
  }

  /// @notice Called by swivel IVFZI && IZFVI
  /// @dev Call with underlying, maturity, mint-target, add-notional-target and an amount
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param z Recipient of the minted zcToken
  /// @param n Recipient of the added notional
  /// @param a Amount of zcToken minted and notional added
  function custodialInitiate(address u, uint256 m, address z, address n, uint256 a) external authorized(swivel) unpaused() returns (bool) {
    Market memory mkt = markets[u][m];
    require(ZcToken(mkt.zcTokenAddr).mint(z, a), 'mint failed');
    require(VaultTracker(mkt.vaultAddr).addNotional(n, a), 'add notional failed');
    emit CustodialInitiate(u, m, z, n, a);
    return true;
  }

  /// @notice Called by swivel EVFZE FF EZFVE
  /// @dev Call with underlying, maturity, burn-target, remove-notional-target and an amount
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param z Owner of the zcToken to be burned
  /// @param n Target to remove notional from
  /// @param a Amount of zcToken burned and notional removed
  function custodialExit(address u, uint256 m, address z, address n, uint256 a) external authorized(swivel) unpaused() returns (bool) {
    Market memory mkt = markets[u][m];
    require(ZcToken(mkt.zcTokenAddr).burn(z, a), 'burn failed');
    require(VaultTracker(mkt.vaultAddr).removeNotional(n, a), 'remove notional failed');
    emit CustodialExit(u, m, z, n, a);
    return true;
  }

  /// @notice Called by swivel IZFZE, EZFZI
  /// @dev Call with underlying, maturity, transfer-from, transfer-to, amount
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param f Owner of the zcToken to be burned
  /// @param t Target to be minted to
  /// @param a Amount of zcToken transfer
  function p2pZcTokenExchange(address u, uint256 m, address f, address t, uint256 a) external authorized(swivel) unpaused() returns (bool) {
    Market memory mkt = markets[u][m];
    require(ZcToken(mkt.zcTokenAddr).burn(f, a), 'zcToken burn failed');
    require(ZcToken(mkt.zcTokenAddr).mint(t, a), 'zcToken mint failed');
    emit P2pZcTokenExchange(u, m, f, t, a);
    return true;
  }

  /// @notice Called by swivel IVFVE, EVFVI
  /// @dev Call with underlying, maturity, remove-from, add-to, amount
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param f Owner of the notional to be transferred
  /// @param t Target to be transferred to
  /// @param a Amount of notional transfer
  function p2pVaultExchange(address u, uint256 m, address f, address t, uint256 a) external authorized(swivel) unpaused() returns (bool) {
    require(VaultTracker(markets[u][m].vaultAddr).transferNotionalFrom(f, t, a), 'transfer notional failed');
    emit P2pVaultExchange(u, m, f, t, a);
    return true;
  }

  /// @notice External method giving access to this functionality within a given vault
  /// @dev Note that this method calculates yield and interest as well
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param t Target to be transferred to
  /// @param a Amount of notional to be transferred
  function transferVaultNotional(address u, uint256 m, address t, uint256 a) external unpaused() returns (bool) {
    require(VaultTracker(markets[u][m].vaultAddr).transferNotionalFrom(msg.sender, t, a), 'vault transfer failed');
    emit TransferVaultNotional(u, m, msg.sender, t, a);
    return true;
  }

  /// @notice Transfers notional fee to the Swivel contract without recalculating marginal interest for from
  /// @param u Underlying token address associated with the market
  /// @param m Maturity timestamp of the market
  /// @param f Owner of the amount
  /// @param a Amount to transfer
  function transferVaultNotionalFee(address u, uint256 m, address f, uint256 a) external authorized(swivel) returns (bool) {
    VaultTracker(markets[u][m].vaultAddr).transferNotionalFee(f, a);
    return true;
  }

  /// @notice Called by admin at any point to pause / unpause market transactions
  /// @param b Boolean which indicates the markets paused status
  function pause(bool b) external authorized(admin) returns (bool) {
    paused = b;
    return true;
  }

  modifier authorized(address a) {
    require(msg.sender == a, 'sender must be authorized');
    _;
  }

  modifier unpaused() {
    require(!paused, 'markets are paused');
    _;
  }
}

// File: PErc20.sol



// SPDX-License-Identifier: MIT

pragma solidity >= 0.8.4;

import "./IPErc20.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * NOTES: This is an adaptation of the Open Zeppelin ERC20, with changes made per audit
 * requests, and to fit overall Swivel Style. We use it specifically as the base for
 * the Erc2612 hence the `Perc` (Permissioned erc20) naming.
 *
 * Dangling underscores are generally not allowed within swivel style but the 
 * internal, abstracted implementation methods inherted from the O.Z contract are maintained here.
 * Hence, when you see a dangling underscore prefix, you know it is *only* allowed for
 * one of these method calls. It is not allowed for any other purpose. These are:
     _approve
     _transfer
     _mint
     _burn
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * We have followed general OpenZeppelin guidelines: functions revert instead
 * of returning `false` on failure. This behavior is nonetheless conventional
 * and does not conflict with the expectations of ERC20 applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.


 */
contract PErc20 is IPErc20 {
    mapping (address => uint256) private balances;
    mapping (address => mapping (address => uint256)) private allowances;

    uint8 public decimals;
    uint256 public totalSupply;
    string public name; // NOTE: cannot make strings immutable
    string public symbol; // NOTE: see above

    /**
     * @dev Sets the values for {name} and {symbol}.
     * @param n Name of the token
     * @param s Symbol of the token
     * @param d Decimals of the token
     */
    constructor (string memory n, string memory s, uint8 d) {
        name = n;
        symbol = s;
        decimals = d;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     * @param a Adress to fetch balance of
     */
    function balanceOf(address a) public view virtual override returns (uint256) {
        return balances[a];
    }

    /**
     * @dev See {IERC20-transfer}.
     * @param r The recipient
     * @param a The amount transferred
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address r, uint256 a) public virtual override returns (bool) {
        _transfer(msg.sender, r, a);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     * @param o The owner
     * @param s The spender
     */
    function allowance(address o, address s) public view virtual override returns (uint256) {
        return allowances[o][s];
    }

    /**
     * @dev See {IERC20-approve}.
     * @param s The spender
     * @param a The amount to approve
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address s, uint256 a) public virtual override returns (bool) {
        _approve(msg.sender, s, a);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * @param s The sender
     * @param r The recipient
     * @param a The amount to transfer
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * Requirements:
     *
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     * - the caller must have allowance for ``sender``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address s, address r, uint256 a) public virtual override returns (bool) {
        _transfer(s, r, a);

        uint256 currentAllowance = allowances[s][msg.sender];
        require(currentAllowance >= a, "erc20 transfer amount exceeds allowance");
        _approve(s, msg.sender, currentAllowance - a);

        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     * @param s The spender
     * @param a The amount increased
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address s, uint256 a) public virtual returns (bool) {
        _approve(msg.sender, s, allowances[msg.sender][s] + a);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     * @param s The spender
     * @param a The amount subtracted
     * 
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address s, uint256 a) public virtual returns (bool) {
        uint256 currentAllowance = allowances[msg.sender][s];
        require(currentAllowance >= a, "erc20 decreased allowance below zero");
        _approve(msg.sender, s, currentAllowance - a);

        return true;
    }

    /**
     * @dev Moves tokens `amount` from `sender` to `recipient`.
     * @param s The sender
     * @param r The recipient
     * @param a The amount to transfer
     *
     * This is internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `sender` cannot be the zero address.
     * - `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `amount`.
     */
    function _transfer(address s, address r, uint256 a) internal virtual {
        require(s != address(0), "erc20 transfer from the zero address");
        require(r != address(0), "erc20 transfer to the zero address");

        uint256 senderBalance = balances[s];
        require(senderBalance >= a, "erc20 transfer amount exceeds balance");
        balances[s] = senderBalance - a;
        balances[r] += a;

        emit Transfer(s, r, a);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     * @param r The recipient
     * @param a The amount to mint
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `recipient` cannot be the zero address.
     */
    function _mint(address r, uint256 a) internal virtual {
        require(r != address(0), "erc20 mint to the zero address");

        totalSupply += a;
        balances[r] += a;
        emit Transfer(address(0), r, a);
    }

    /**
     * @dev Destroys `amount` tokens from `owner`, reducing the
     * total supply.
     * @param o The owner of the amount being burned
     * @param a The amount to burn
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `owner` must have at least `amount` tokens.
     */
    function _burn(address o, uint256 a) internal virtual {
        require(o != address(0), "erc20 burn from the zero address");

        uint256 accountBalance = balances[o];
        require(accountBalance >= a, "erc20 burn amount exceeds balance");
        balances[o] = accountBalance - a;
        totalSupply -= a;

        emit Transfer(o, address(0), a);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     * @param o The owner
     * @param s The spender
     * @param a The amount
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address o, address s, uint256 a) internal virtual {
        require(o != address(0), "erc20 approve from the zero address");
        require(s != address(0), "erc20 approve to the zero address");

        allowances[o][s] = a;
        emit Approval(o, s, a);
    }
}

// File: VaultTracker.sol



// SPDX-License-Identifier: UNLICENSED

pragma solidity >= 0.8.4;

import './Interfaces.sol';

contract VaultTracker {
  struct Vault {
    uint256 notional;
    uint256 redeemable;
    uint256 exchangeRate;
  }

  mapping(address => Vault) public vaults;

  address public immutable admin;
  address public immutable cTokenAddr;
  address public immutable swivel;
  uint256 public immutable maturity;
  uint256 public maturityRate;

  /// @param m Maturity timestamp of the new market
  /// @param c cToken address associated with underlying for the new market
  /// @param s address of the deployed swivel contract
  constructor(uint256 m, address c, address s) {
    admin = msg.sender;
    maturity = m;
    cTokenAddr = c;
    swivel = s;

    // instantiate swivel's vault (unblocking transferNotionalFee)
    vaults[s] = Vault({
      notional: 0,
      redeemable: 0,
      exchangeRate: CErc20(c).exchangeRateCurrent()
    });
  }

  /// @notice Adds notional to a given address
  /// @param o Address that owns a vault
  /// @param a Amount of notional added
  function addNotional(address o, uint256 a) external authorized(admin) returns (bool) {
    uint256 exchangeRate = CErc20(cTokenAddr).exchangeRateCurrent();

    Vault memory vlt = vaults[o];

    if (vlt.notional > 0) {
      uint256 yield;

      // if market has matured, calculate marginal interest between the maturity rate and previous position exchange rate
      // otherwise, calculate marginal exchange rate between current and previous exchange rate.
      if (maturityRate > 0) { // Calculate marginal interest
        yield = ((maturityRate * 1e26) / vlt.exchangeRate) - 1e26;
      } else {
        yield = ((exchangeRate * 1e26) / vlt.exchangeRate) - 1e26;
      }

      uint256 interest = (yield * vlt.notional) / 1e26;
      // add interest and amount to position, reset cToken exchange rate
      vlt.redeemable += interest;
      vlt.notional += a;
    } else {
      vlt.notional = a;
    }

    vlt.exchangeRate = exchangeRate;
    vaults[o] = vlt;

    return true;
  }

  /// @notice Removes notional from a given address
  /// @param o Address that owns a vault
  /// @param a Amount of notional to remove
  function removeNotional(address o, uint256 a) external authorized(admin) returns (bool) {

    Vault memory vlt = vaults[o];

    require(vlt.notional >= a, "amount exceeds vault balance");

    uint256 yield;
    uint256 exchangeRate = CErc20(cTokenAddr).exchangeRateCurrent();

    // if market has matured, calculate marginal interest between the maturity rate and previous position exchange rate
    // otherwise, calculate marginal exchange rate between current and previous exchange rate.
    if (maturityRate > 0) { // Calculate marginal interest
      yield = ((maturityRate * 1e26) / vlt.exchangeRate) - 1e26;
    } else {
      // calculate marginal interest
      yield = ((exchangeRate * 1e26) / vlt.exchangeRate) - 1e26;
    }

    uint256 interest = (yield * vlt.notional) / 1e26;
    // remove amount from position, Add interest to position, reset cToken exchange rate
    vlt.redeemable += interest;
    vlt.notional -= a;
    vlt.exchangeRate = exchangeRate;

    vaults[o] = vlt;

    return true;
  }

  /// @notice Redeem's interest accrued by a given address
  /// @param o Address that owns a vault
  function redeemInterest(address o) external authorized(admin) returns (uint256) {

    Vault memory vlt = vaults[o];

    uint256 redeemable = vlt.redeemable;
    uint256 yield;
    uint256 exchangeRate = CErc20(cTokenAddr).exchangeRateCurrent();

    // if market has matured, calculate marginal interest between the maturity rate and previous position exchange rate
    // otherwise, calculate marginal exchange rate between current and previous exchange rate.
    if (maturityRate > 0) { // Calculate marginal interest
      yield = ((maturityRate * 1e26) / vlt.exchangeRate) - 1e26;
    } else {
      // calculate marginal interest
      yield = ((exchangeRate * 1e26) / vlt.exchangeRate) - 1e26;
    }

    uint256 interest = (yield * vlt.notional) / 1e26;

    vlt.exchangeRate = exchangeRate;
    vlt.redeemable = 0;

    vaults[o] = vlt;

    // return adds marginal interest to previously accrued redeemable interest
    return (redeemable + interest);
  }

  /// @notice Matures the vault
  /// @param c The current cToken exchange rate
  function matureVault(uint256 c) external authorized(admin) returns (bool) {
    maturityRate = c;
    return true;
  }

  /// @notice Transfers notional from one address to another
  /// @param f Owner of the amount
  /// @param t Recipient of the amount
  /// @param a Amount to transfer
  function transferNotionalFrom(address f, address t, uint256 a) external authorized(admin) returns (bool) {
    require(f != t, 'cannot transfer notional to self');

    Vault memory from = vaults[f];
    Vault memory to = vaults[t];

    require(from.notional >= a, "amount exceeds available balance");

    uint256 yield;
    uint256 exchangeRate = CErc20(cTokenAddr).exchangeRateCurrent();

    // if market has matured, calculate marginal interest between the maturity rate and previous position exchange rate
    // otherwise, calculate marginal exchange rate between current and previous exchange rate.
    if (maturityRate > 0) { 
      // calculate marginal interest
      yield = ((maturityRate * 1e26) / from.exchangeRate) - 1e26;
    } else {
      yield = ((exchangeRate * 1e26) / from.exchangeRate) - 1e26;
    }

    uint256 interest = (yield * from.notional) / 1e26;
    // remove amount from position, Add interest to position, reset cToken exchange rate
    from.redeemable += interest;
    from.notional -= a;
    from.exchangeRate = exchangeRate;

    vaults[f] = from;

    // transfer notional to address "t", calculate interest if necessary
    if (to.notional > 0) {
      // if market has matured, calculate marginal interest between the maturity rate and previous position exchange rate
      // otherwise, calculate marginal exchange rate between current and previous exchange rate.
      if (maturityRate > 0) { 
        // calculate marginal interest
        yield = ((maturityRate * 1e26) / to.exchangeRate) - 1e26;
      } else {
        yield = ((exchangeRate * 1e26) / to.exchangeRate) - 1e26;
      }

      uint256 newVaultInterest = (yield * to.notional) / 1e26;
      // add interest and amount to position, reset cToken exchange rate
      to.redeemable += newVaultInterest;
      to.notional += a;
    } else {
      to.notional = a;
    }

    to.exchangeRate = exchangeRate;
    vaults[t] = to;

    return true;
  }

  /// @notice Transfers, in notional, a fee payment to the Swivel contract without recalculating marginal interest for the owner
  /// @param f Owner of the amount
  /// @param a Amount to transfer
  function transferNotionalFee(address f, uint256 a) external authorized(admin) returns(bool) {
    Vault memory oVault = vaults[f];
    Vault memory sVault = vaults[swivel];

    // remove notional from its owner
    oVault.notional -= a;

    uint256 exchangeRate = CErc20(cTokenAddr).exchangeRateCurrent();
    uint256 yield;

    // check if exchangeRate has been stored already this block. If not, calculate marginal interest + store exchangeRate
    if (sVault.exchangeRate != exchangeRate) {
      // if market has matured, calculate marginal interest between the maturity rate and previous position exchange rate
      // otherwise, calculate marginal exchange rate between current and previous exchange rate.
      if (maturityRate > 0) { 
        // calculate marginal interest
          yield = ((maturityRate * 1e26) / sVault.exchangeRate) - 1e26;
      } else {
          yield = ((exchangeRate * 1e26) / sVault.exchangeRate) - 1e26;
      }
      uint256 interest = (yield * sVault.notional) / 1e26;
      // add interest and amount, reset cToken exchange rate
      sVault.redeemable += interest;
      sVault.exchangeRate = exchangeRate;
    }
    // add notional to swivel's vault
    sVault.notional += a;
    // store the adjusted vaults
    vaults[swivel] = sVault;
    vaults[f] = oVault;
    return true;
  }

  /// @notice Returns both relevant balances for a given user's vault
  /// @param o Address that owns a vault
  function balancesOf(address o) external view returns (uint256, uint256) {
    Vault memory vault = vaults[o];
    return (vault.notional, vault.redeemable);
  }

  modifier authorized(address a) {
    require(msg.sender == a, 'sender must be authorized');
    _;
  }
}

// File: ZcToken.sol



// SPDX-License-Identifier: MIT

// NOTE: whomever decided methods which are the implementation of a stated interface
//       need to be labeled 'override' should to be publicly flogged.

pragma solidity >= 0.8.4;

import './Erc2612.sol';
import './IZcToken.sol';

/// NOTE the OZStlye naming conventions are kept for the internal methods
/// _burn and _mint as dangling underscores are generally not allowed.
contract ZcToken is Erc2612, IZcToken {
  address public immutable admin;
  address public immutable underlying;
  uint256 public immutable maturity;

  /// @param u Underlying
  /// @param m Maturity
  /// @param n Name
  /// @param s Symbol
  /// @param d Decimals
  constructor(address u, uint256 m, string memory n, string memory s, uint8 d) Erc2612(n, s, d) {
    admin = msg.sender;  
    underlying = u;
    maturity = m;
  }
  
  /// @param f Address to burn from
  /// @param a Amount to burn
  function burn(address f, uint256 a) external onlyAdmin(admin) override returns(bool) {
      _burn(f, a);
      return true;
  }

  /// @dev Muturity must not have been reached else we revert here.
  /// @param t Address recieving the minted amount
  /// @param a The amount to mint
  function mint(address t, uint256 a) external onlyAdmin(admin) override returns(bool) {
    require(block.timestamp < maturity, 'maturity reached');
    _mint(t, a);
    return true;
  }

  /// @param a Admin address
  modifier onlyAdmin(address a) {
    require(msg.sender == a, 'sender must be admin');
    _;
  }
}