contract TokenizedDerivative is ERC20, AdministrateeInterface, ExpandedIERC20 {
    using TokenizedDerivativeUtils for TDS.Storage;

    TDS.Storage public derivativeStorage;

    // These events are actually emitted by TokenizedDerivativeUtils, but we unfortunately have to define the events
    // here as well.
    event NavUpdated(string symbol, int256 newNav, int256 newTokenPrice);
    event Default(string symbol, uint256 defaultTime, int256 defaultNav);
    event Settled(string symbol, uint256 settleTime, int256 finalNav);
    event Expired(string symbol, uint256 expiryTime);
    event Disputed(string symbol, uint256 timeDisputed, int256 navDisputed);
    event EmergencyShutdownTransition(string symbol, uint256 shutdownTime);
    event TokensCreated(string symbol, uint256 numTokensCreated);
    event TokensRedeemed(string symbol, uint256 numTokensRedeemed);
    event Deposited(string symbol, uint256 amount);
    event Withdrawal(string symbol, uint256 amount);

    constructor(TokenizedDerivativeParams.ConstructorParams memory params, string memory _name, string memory _symbol)
        public
        ERC20(_name, _symbol)
    {
        // Initialize the contract.
        derivativeStorage._initialize(params, _symbol);
    }

    /**
     * @notice Creates tokens with sent margin and sends the caller back any additional margin.
     * @param marginForPurchase Maximum amount of margin currency to use to create tokens.
     * @param tokensToPurchase Number of tokens to create.
     */
    function createTokens(uint256 marginForPurchase, uint256 tokensToPurchase) external payable {
        derivativeStorage._createTokens(marginForPurchase, tokensToPurchase);
    }

    /**
     * @notice Creates tokens with sent margin and deposits additional margin in short account.
     * @param marginForPurchase Maximum amount of margin currency to use to create tokens.
     * @param tokensToPurchase Number of tokens to create.
     */
    function depositAndCreateTokens(uint256 marginForPurchase, uint256 tokensToPurchase) external payable {
        derivativeStorage._depositAndCreateTokens(marginForPurchase, tokensToPurchase);
    }

    /**
     * @notice Redeems tokens for margin currency.
     */
    function redeemTokens(uint256 tokensToRedeem) external {
        derivativeStorage._redeemTokens(tokensToRedeem);
    }

    /**
     * @notice Triggers a price dispute for the most recent remargin time.
     * @param depositMargin Must be at least `disputeDeposit` percent of the required margin.
     */
    function dispute(uint256 depositMargin) external payable {
        derivativeStorage._dispute(depositMargin);
    }

    /**
     * @notice Withdraws `amount` from short margin account.
     */
    function withdraw(uint256 amount) external {
        derivativeStorage._withdraw(amount);
    }

    /**
     * @notice Pays (Oracle and service) fees for the previous period, updates the contract NAV, moves margin between
     * long and short accounts to reflect the new NAV, and checks if both accounts meet minimum requirements.
     */
    // TODO(#969) Remove once prettier-plugin-solidity can handle the "override" keyword
    // prettier-ignore
    function remargin() external override {
        derivativeStorage._remargin();
    }

    /**
     * @notice Forgo the Oracle verified price and settle the contract with last remargin price.
     * @dev This method is only callable on contracts in the `Defaulted` state, and the default penalty is always
     * transferred from the short to the long account.
     */
    function acceptPriceAndSettle() external {
        derivativeStorage._acceptPriceAndSettle();
    }

    /**
     * @notice Assigns an address to be the contract's Delegate AP that can create and redeem.
     * @dev Replaces previous value. Set to 0x0 to indicate there is no Delegate AP.
     */
    function setApDelegate(address apDelegate) external {
        derivativeStorage._setApDelegate(apDelegate);
    }

    /**
     * @notice Moves the contract into the Emergency state, where it waits on an Oracle price for the most recent
     * remargin time.
     */
    // TODO(#969) Remove once prettier-plugin-solidity can handle the "override" keyword
    // prettier-ignore
    function emergencyShutdown() external override {
        derivativeStorage._emergencyShutdown();
    }

    /**
     * @notice Performs a final remargin, assesses any penalties, and moves the contract into the `Settled` state. An
     * Oracle price must be available.
     */
    function settle() external {
        derivativeStorage._settle();
    }

    /**
     * @notice Adds the margin to the short account.
     * @dev For ETH-margined contracts, send ETH along with the call. In the case of an ERC20 margin currency, authorize
     * before calling this method.
     */
    function deposit(uint256 amountToDeposit) external payable {
        derivativeStorage._deposit(amountToDeposit);
    }

    /**
     * @notice Withdraw any ERC20 balance that is not the margin token. Only callable by the sponsor.
     */
    function withdrawUnexpectedErc20(address erc20Address, uint256 amount) external {
        derivativeStorage._withdrawUnexpectedErc20(erc20Address, amount);
    }

    // ExpandedIERC20 methods.
    modifier onlyThis {
        require(msg.sender == address(this));
        _;
    }

    /**
     * @notice Destroys `value` tokens from the caller.
     * @dev Only this contract or its libraries are allowed to burn tokens.
     */
    // TODO(#969) Remove once prettier-plugin-solidity can handle the "override" keyword
    // prettier-ignore
    function burn(uint256 value) external override onlyThis {
        _burn(msg.sender, value);
    }

    /**
     * @notice Creates `value` tokens and assigns them to `to`, increasing the total supply.
     * @dev Only this contract or its libraries are allowed to mint tokens.
     */
    // TODO(#969) Remove once prettier-plugin-solidity can handle the "override" keyword
    // prettier-ignore
    function mint(address to, uint256 value) external override onlyThis returns (bool) {
        _mint(to, value);
        return true;
    }

    /**
     * @notice Returns the expected net asset value (NAV) of the contract using the latest available Price Feed price.
     */
    function calcNAV() external view returns (int256 navNew) {
        return derivativeStorage._calcNAV();
    }

    /**
     * @notice Returns the expected value of each the outstanding tokens of the contract using the latest available
     * Price Feed price.
     */
    function calcTokenValue() external view returns (int256 newTokenValue) {
        return derivativeStorage._calcTokenValue();
    }

    /**
     * @notice Returns the expected balance of the short margin account using the latest available Price Feed price.
     */
    function calcShortMarginBalance() external view returns (int256 newShortMarginBalance) {
        return derivativeStorage._calcShortMarginBalance();
    }

    /**
     * @notice Returns the expected short margin in excess of the margin requirement using the latest available Price
     * Feed price.
     * @dev Value will be negative if the short margin is expected to be below the margin requirement.
     */
    function calcExcessMargin() external view returns (int256 excessMargin) {
        return derivativeStorage._calcExcessMargin();
    }

    /**
     * @notice Returns the required margin, as of the last remargin.
     * @dev Note that `calcExcessMargin` uses updated values using the latest available Price Feed price.
     */
    function getCurrentRequiredMargin() external view returns (int256 requiredMargin) {
        return derivativeStorage._getCurrentRequiredMargin();
    }

    /**
     * @notice Returns whether the contract can be settled, i.e., is it valid to call settle() now.
     */
    function canBeSettled() external view returns (bool canContractBeSettled) {
        return derivativeStorage._canBeSettled();
    }

    /**
     * @notice Returns the updated underlying price that was used in the calc* methods above.
     * @dev It will be a price feed price if the contract is Live and will remain Live, or an Oracle price if the
     * contract is settled/about to be settled.  Reverts if no Oracle price is available but an Oracle price is
     * required.
     */
    function getUpdatedUnderlyingPrice() external view returns (int256 underlyingPrice, uint256 time) {
        return derivativeStorage._getUpdatedUnderlyingPrice();
    }
}
/**
 * @title A synthetic token whose value tracks an arbitrary price feed.
 */
function calcShortMarginBalance() external view returns (int256 newShortMarginBalance) {
    return derivativeStorage._calcShortMarginBalance();
}

/**
 * @notice Returns the expected balance of the short margin account using the latest available Price Feed price.
 */